Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Converting JSON to XML using XSLT 1.0

Status
Not open for further replies.

k5tm

Programmer
Aug 24, 2001
2,038
0
0
US
Background: A customer with robust XML capability needs to interact with a well known online payment system that uses JSON as its lingua franca. There is no readily available, easy-to-use JSON parser on the system. The challenge to me was to see if I could somehow use XSLT to process the JSON response package. (Producing JSON using XSLT is almost trivially easy.) The customer's XML engine, provided by a major software vendor, is limited to XSLT 1.0.

I did quite a bit of online searching for a solution. At least two elegant solutions exist that use XSLT 2.0, but I was unable to find a reliable solution using XSLT 1.0; what I could find makes use of extensions, which I was also trying to avoid.

I have developed a two step solution which uses standard XSLT 1.0, without extensions.

The first step makes extensive use of recursion to do a left-to-right scan of a JSON string, using output method="text" to create a well-formed XML document that essentially restates the JSON, albiet in rather odd-looking XML.

The second step is a normal XML-to-XML transform that restructures the output of the first step into a more pleasing, and probably more useful, XML document.

There are some caveats.

JSON descriptions talk quite a bit about name:value pairs, but you should be aware that what JSON describes as a name is really just a string without any additional requirements. While most examples of JSON use name strings that can be directly translated to XML names (as defined in the W3C specification for XML), be aware that some modification of the JSON name string value may be necessary. In the illustrated process, this name manipulation is handled in the second step.

JSON strings allow escaped characters. In particular, the quote character (") can be embedded in a string using the notation \". In the illustrated process, this is not allowed and will result in output from the first step that is not well-formed. My customer did not need this capability, and I did not provide it in order to reduce recursion depth. One could treat strings in a manner similar to the way numerics are treated. Ask if you need help.

Here is the result from an example I found on json.org, a site that shows considerable anti-XML bigotry:
JSON:
{"web-app": {
  "servlet": [   
    {
      "servlet-name": "cofaxCDS",
      "servlet-class": "org.cofax.cds.CDSServlet",
      "init-param": {
        "configGlossary:installationAt": "Philadelphia, PA",
        "configGlossary:adminEmail": "ksm@pobox.com",
        "configGlossary:poweredBy": "Cofax",
        "configGlossary:poweredByIcon": "/images/cofax.gif",
        "configGlossary:staticPath": "/content/static",
        "templateProcessorClass": "org.cofax.WysiwygTemplate",
        "templateLoaderClass": "org.cofax.FilesTemplateLoader",
        "templatePath": "templates",
        "templateOverridePath": "",
        "defaultListTemplate": "listTemplate.htm",
        "defaultFileTemplate": "articleTemplate.htm",
        "useJSP": false,
        "jspListTemplate": "listTemplate.jsp",
        "jspFileTemplate": "articleTemplate.jsp",
        "cachePackageTagsTrack": 200,
        "cachePackageTagsStore": 200,
        "cachePackageTagsRefresh": 60,
        "cacheTemplatesTrack": 100,
        "cacheTemplatesStore": 50,
        "cacheTemplatesRefresh": 15,
        "cachePagesTrack": 200,
        "cachePagesStore": 100,
        "cachePagesRefresh": 10,
        "cachePagesDirtyRead": 10,
        "searchEngineListTemplate": "forSearchEnginesList.htm",
        "searchEngineFileTemplate": "forSearchEngines.htm",
        "searchEngineRobotsDb": "WEB-INF/robots.db",
        "useDataStore": true,
        "dataStoreClass": "org.cofax.SqlDataStore",
        "redirectionClass": "org.cofax.SqlRedirection",
        "dataStoreName": "cofax",
        "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
        "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
        "dataStoreUser": "sa",
        "dataStorePassword": "dataStoreTestQuery",
        "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
        "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
        "dataStoreInitConns": 10,
        "dataStoreMaxConns": 100,
        "dataStoreConnUsageLimit": 100,
        "dataStoreLogLevel": "debug",
        "maxUrlLength": 500}},
    {
      "servlet-name": "cofaxEmail",
      "servlet-class": "org.cofax.cds.EmailServlet",
      "init-param": {
      "mailHost": "mail1",
      "mailHostOverride": "mail2"}},
    {
      "servlet-name": "cofaxAdmin",
      "servlet-class": "org.cofax.cds.AdminServlet"},
 
    {
      "servlet-name": "fileServlet",
      "servlet-class": "org.cofax.cds.FileServlet"},
    {
      "servlet-name": "cofaxTools",
      "servlet-class": "org.cofax.cms.CofaxToolsServlet",
      "init-param": {
        "templatePath": "toolstemplates/",
        "log": 1,
        "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
        "logMaxSize": "",
        "dataLog": 1,
        "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
        "dataLogMaxSize": "",
        "removePageCache": "/content/admin/remove?cache=pages&id=",
        "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
        "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
        "lookInContext": 1,
        "adminGroupID": 4,
        "betaServer": true}}],
  "servlet-mapping": {
    "cofaxCDS": "/",
    "cofaxEmail": "/cofaxutil/aemail/*",
    "cofaxAdmin": "/admin/*",
    "fileServlet": "/static/*",
    "cofaxTools": "/tools/*"},
 
  "taglib": {
    "taglib-uri": "cofax.tld",
    "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
Code:
<root>
    <object>
        <nameNode name="web-app"/>
        <object>
            <nameNode name="servlet"/>
            <array>
                <object>
                    <nameNode name="servlet-name"/>
                    <value type="string">
                        <![CDATA[cofaxCDS]]>
                    </value>
                    <nameNode name="servlet-class"/>
                    <value type="string">
                        <![CDATA[org.cofax.cds.CDSServlet]]>
                    </value>
                    <nameNode name="init-param"/>
                    <object>
                        <nameNode name="configGlossary:installationAt"/>
                        <value type="string">
                            <![CDATA[Philadelphia, PA]]>
                        </value>
                        <nameNode name="configGlossary:adminEmail"/>
                        <value type="string">
                            <![CDATA[ksm@pobox.com]]>
                        </value>
                        <nameNode name="configGlossary:poweredBy"/>
                        <value type="string">
                            <![CDATA[Cofax]]>
                        </value>
                        <nameNode name="configGlossary:poweredByIcon"/>
                        <value type="string">
                            <![CDATA[/images/cofax.gif]]>
                        </value>
                        <nameNode name="configGlossary:staticPath"/>
                        <value type="string">
                            <![CDATA[/content/static]]>
                        </value>
                        <nameNode name="templateProcessorClass"/>
                        <value type="string">
                            <![CDATA[org.cofax.WysiwygTemplate]]>
                        </value>
                        <nameNode name="templateLoaderClass"/>
                        <value type="string">
                            <![CDATA[org.cofax.FilesTemplateLoader]]>
                        </value>
                        <nameNode name="templatePath"/>
                        <value type="string">
                            <![CDATA[templates]]>
                        </value>
                        <nameNode name="templateOverridePath"/>
                        <value type="string">
                            <![CDATA[]]>
                        </value>
                        <nameNode name="defaultListTemplate"/>
                        <value type="string">
                            <![CDATA[listTemplate.htm]]>
                        </value>
                        <nameNode name="defaultFileTemplate"/>
                        <value type="string">
                            <![CDATA[articleTemplate.htm]]>
                        </value>
                        <nameNode name="useJSP"/>
                        <value type="false">false</value>
                        <nameNode name="jspListTemplate"/>
                        <value type="string">
                            <![CDATA[listTemplate.jsp]]>
                        </value>
                        <nameNode name="jspFileTemplate"/>
                        <value type="string">
                            <![CDATA[articleTemplate.jsp]]>
                        </value>
                        <nameNode name="cachePackageTagsTrack"/>
                        <value type="number">200</value>
                        <nameNode name="cachePackageTagsStore"/>
                        <value type="number">200</value>
                        <nameNode name="cachePackageTagsRefresh"/>
                        <value type="number">60</value>
                        <nameNode name="cacheTemplatesTrack"/>
                        <value type="number">100</value>
                        <nameNode name="cacheTemplatesStore"/>
                        <value type="number">50</value>
                        <nameNode name="cacheTemplatesRefresh"/>
                        <value type="number">15</value>
                        <nameNode name="cachePagesTrack"/>
                        <value type="number">200</value>
                        <nameNode name="cachePagesStore"/>
                        <value type="number">100</value>
                        <nameNode name="cachePagesRefresh"/>
                        <value type="number">10</value>
                        <nameNode name="cachePagesDirtyRead"/>
                        <value type="number">10</value>
                        <nameNode name="searchEngineListTemplate"/>
                        <value type="string">
                            <![CDATA[forSearchEnginesList.htm]]>
                        </value>
                        <nameNode name="searchEngineFileTemplate"/>
                        <value type="string">
                            <![CDATA[forSearchEngines.htm]]>
                        </value>
                        <nameNode name="searchEngineRobotsDb"/>
                        <value type="string">
                            <![CDATA[WEB-INF/robots.db]]>
                        </value>
                        <nameNode name="useDataStore"/>
                        <value type="true">true</value>
                        <nameNode name="dataStoreClass"/>
                        <value type="string">
                            <![CDATA[org.cofax.SqlDataStore]]>
                        </value>
                        <nameNode name="redirectionClass"/>
                        <value type="string">
                            <![CDATA[org.cofax.SqlRedirection]]>
                        </value>
                        <nameNode name="dataStoreName"/>
                        <value type="string">
                            <![CDATA[cofax]]>
                        </value>
                        <nameNode name="dataStoreDriver"/>
                        <value type="string">
                            <![CDATA[com.microsoft.jdbc.sqlserver.SQLServerDriver]]>
                        </value>
                        <nameNode name="dataStoreUrl"/>
                        <value type="string">
                            <![CDATA[jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon]]>
                        </value>
                        <nameNode name="dataStoreUser"/>
                        <value type="string">
                            <![CDATA[sa]]>
                        </value>
                        <nameNode name="dataStorePassword"/>
                        <value type="string">
                            <![CDATA[dataStoreTestQuery]]>
                        </value>
                        <nameNode name="dataStoreTestQuery"/>
                        <value type="string">
                            <![CDATA[SET NOCOUNT ON;select test='test';]]>
                        </value>
                        <nameNode name="dataStoreLogFile"/>
                        <value type="string">
                            <![CDATA[/usr/local/tomcat/logs/datastore.log]]>
                        </value>
                        <nameNode name="dataStoreInitConns"/>
                        <value type="number">10</value>
                        <nameNode name="dataStoreMaxConns"/>
                        <value type="number">100</value>
                        <nameNode name="dataStoreConnUsageLimit"/>
                        <value type="number">100</value>
                        <nameNode name="dataStoreLogLevel"/>
                        <value type="string">
                            <![CDATA[debug]]>
                        </value>
                        <nameNode name="maxUrlLength"/>
                        <value type="number">500</value>
                    </object>
                </object>
                <object>
                    <nameNode name="servlet-name"/>
                    <value type="string">
                        <![CDATA[cofaxEmail]]>
                    </value>
                    <nameNode name="servlet-class"/>
                    <value type="string">
                        <![CDATA[org.cofax.cds.EmailServlet]]>
                    </value>
                    <nameNode name="init-param"/>
                    <object>
                        <nameNode name="mailHost"/>
                        <value type="string">
                            <![CDATA[mail1]]>
                        </value>
                        <nameNode name="mailHostOverride"/>
                        <value type="string">
                            <![CDATA[mail2]]>
                        </value>
                    </object>
                </object>
                <object>
                    <nameNode name="servlet-name"/>
                    <value type="string">
                        <![CDATA[cofaxAdmin]]>
                    </value>
                    <nameNode name="servlet-class"/>
                    <value type="string">
                        <![CDATA[org.cofax.cds.AdminServlet]]>
                    </value>
                </object>
                <object>
                    <nameNode name="servlet-name"/>
                    <value type="string">
                        <![CDATA[fileServlet]]>
                    </value>
                    <nameNode name="servlet-class"/>
                    <value type="string">
                        <![CDATA[org.cofax.cds.FileServlet]]>
                    </value>
                </object>
                <object>
                    <nameNode name="servlet-name"/>
                    <value type="string">
                        <![CDATA[cofaxTools]]>
                    </value>
                    <nameNode name="servlet-class"/>
                    <value type="string">
                        <![CDATA[org.cofax.cms.CofaxToolsServlet]]>
                    </value>
                    <nameNode name="init-param"/>
                    <object>
                        <nameNode name="templatePath"/>
                        <value type="string">
                            <![CDATA[toolstemplates/]]>
                        </value>
                        <nameNode name="log"/>
                        <value type="number">1</value>
                        <nameNode name="logLocation"/>
                        <value type="string">
                            <![CDATA[/usr/local/tomcat/logs/CofaxTools.log]]>
                        </value>
                        <nameNode name="logMaxSize"/>
                        <value type="string">
                            <![CDATA[]]>
                        </value>
                        <nameNode name="dataLog"/>
                        <value type="number">1</value>
                        <nameNode name="dataLogLocation"/>
                        <value type="string">
                            <![CDATA[/usr/local/tomcat/logs/dataLog.log]]>
                        </value>
                        <nameNode name="dataLogMaxSize"/>
                        <value type="string">
                            <![CDATA[]]>
                        </value>
                        <nameNode name="removePageCache"/>
                        <value type="string">
                            <![CDATA[/content/admin/remove?cache=pages&id=]]>
                        </value>
                        <nameNode name="removeTemplateCache"/>
                        <value type="string">
                            <![CDATA[/content/admin/remove?cache=templates&id=]]>
                        </value>
                        <nameNode name="fileTransferFolder"/>
                        <value type="string">
                            <![CDATA[/usr/local/tomcat/webapps/content/fileTransferFolder]]>
                        </value>
                        <nameNode name="lookInContext"/>
                        <value type="number">1</value>
                        <nameNode name="adminGroupID"/>
                        <value type="number">4</value>
                        <nameNode name="betaServer"/>
                        <value type="true">true</value>
                    </object>
                </object>
            </array>
            <nameNode name="servlet-mapping"/>
            <object>
                <nameNode name="cofaxCDS"/>
                <value type="string">
                    <![CDATA[/]]>
                </value>
                <nameNode name="cofaxEmail"/>
                <value type="string">
                    <![CDATA[/cofaxutil/aemail/*]]>
                </value>
                <nameNode name="cofaxAdmin"/>
                <value type="string">
                    <![CDATA[/admin/*]]>
                </value>
                <nameNode name="fileServlet"/>
                <value type="string">
                    <![CDATA[/static/*]]>
                </value>
                <nameNode name="cofaxTools"/>
                <value type="string">
                    <![CDATA[/tools/*]]>
                </value>
            </object>
            <nameNode name="taglib"/>
            <object>
                <nameNode name="taglib-uri"/>
                <value type="string">
                    <![CDATA[cofax.tld]]>
                </value>
                <nameNode name="taglib-location"/>
                <value type="string">
                    <![CDATA[/WEB-INF/tlds/cofax.tld]]>
                </value>
            </object>
        </object>
    </object>
</root>
Code:
<root>
    <object>
        <web-app>
            <servlet>
                <object>
                    <servlet-name>cofaxCDS</servlet-name>
                    <servlet-class>org.cofax.cds.CDSServlet</servlet-class>
                    <init-param>
                        <configGlossary-installationAt>Philadelphia, PA</configGlossary-installationAt>
                        <configGlossary-adminEmail>ksm@pobox.com</configGlossary-adminEmail>
                        <configGlossary-poweredBy>Cofax</configGlossary-poweredBy>
                        <configGlossary-poweredByIcon>/images/cofax.gif</configGlossary-poweredByIcon>
                        <configGlossary-staticPath>/content/static</configGlossary-staticPath>
                        <templateProcessorClass>org.cofax.WysiwygTemplate</templateProcessorClass>
                        <templateLoaderClass>org.cofax.FilesTemplateLoader</templateLoaderClass>
                        <templatePath>templates</templatePath>
                        <templateOverridePath/>
                        <defaultListTemplate>listTemplate.htm</defaultListTemplate>
                        <defaultFileTemplate>articleTemplate.htm</defaultFileTemplate>
                        <useJSP>false</useJSP>
                        <jspListTemplate>listTemplate.jsp</jspListTemplate>
                        <jspFileTemplate>articleTemplate.jsp</jspFileTemplate>
                        <cachePackageTagsTrack>200</cachePackageTagsTrack>
                        <cachePackageTagsStore>200</cachePackageTagsStore>
                        <cachePackageTagsRefresh>60</cachePackageTagsRefresh>
                        <cacheTemplatesTrack>100</cacheTemplatesTrack>
                        <cacheTemplatesStore>50</cacheTemplatesStore>
                        <cacheTemplatesRefresh>15</cacheTemplatesRefresh>
                        <cachePagesTrack>200</cachePagesTrack>
                        <cachePagesStore>100</cachePagesStore>
                        <cachePagesRefresh>10</cachePagesRefresh>
                        <cachePagesDirtyRead>10</cachePagesDirtyRead>
                        <searchEngineListTemplate>forSearchEnginesList.htm</searchEngineListTemplate>
                        <searchEngineFileTemplate>forSearchEngines.htm</searchEngineFileTemplate>
                        <searchEngineRobotsDb>WEB-INF/robots.db</searchEngineRobotsDb>
                        <useDataStore>true</useDataStore>
                        <dataStoreClass>org.cofax.SqlDataStore</dataStoreClass>
                        <redirectionClass>org.cofax.SqlRedirection</redirectionClass>
                        <dataStoreName>cofax</dataStoreName>
                        <dataStoreDriver>com.microsoft.jdbc.sqlserver.SQLServerDriver</dataStoreDriver>
                        <dataStoreUrl>jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon</dataStoreUrl>
                        <dataStoreUser>sa</dataStoreUser>
                        <dataStorePassword>dataStoreTestQuery</dataStorePassword>
                        <dataStoreTestQuery>SET NOCOUNT ON;select test='test';</dataStoreTestQuery>
                        <dataStoreLogFile>/usr/local/tomcat/logs/datastore.log</dataStoreLogFile>
                        <dataStoreInitConns>10</dataStoreInitConns>
                        <dataStoreMaxConns>100</dataStoreMaxConns>
                        <dataStoreConnUsageLimit>100</dataStoreConnUsageLimit>
                        <dataStoreLogLevel>debug</dataStoreLogLevel>
                        <maxUrlLength>500</maxUrlLength>
                    </init-param>
                </object>
                <object>
                    <servlet-name>cofaxEmail</servlet-name>
                    <servlet-class>org.cofax.cds.EmailServlet</servlet-class>
                    <init-param>
                        <mailHost>mail1</mailHost>
                        <mailHostOverride>mail2</mailHostOverride>
                    </init-param>
                </object>
                <object>
                    <servlet-name>cofaxAdmin</servlet-name>
                    <servlet-class>org.cofax.cds.AdminServlet</servlet-class>
                </object>
                <object>
                    <servlet-name>fileServlet</servlet-name>
                    <servlet-class>org.cofax.cds.FileServlet</servlet-class>
                </object>
                <object>
                    <servlet-name>cofaxTools</servlet-name>
                    <servlet-class>org.cofax.cms.CofaxToolsServlet</servlet-class>
                    <init-param>
                        <templatePath>toolstemplates/</templatePath>
                        <log>1</log>
                        <logLocation>/usr/local/tomcat/logs/CofaxTools.log</logLocation>
                        <logMaxSize/>
                        <dataLog>1</dataLog>
                        <dataLogLocation>/usr/local/tomcat/logs/dataLog.log</dataLogLocation>
                        <dataLogMaxSize/>
                        <removePageCache>/content/admin/remove?cache=pages&amp;id=</removePageCache>
                        <removeTemplateCache>/content/admin/remove?cache=templates&amp;id=</removeTemplateCache>
                        <fileTransferFolder>/usr/local/tomcat/webapps/content/fileTransferFolder</fileTransferFolder>
                        <lookInContext>1</lookInContext>
                        <adminGroupID>4</adminGroupID>
                        <betaServer>true</betaServer>
                    </init-param>
                </object>
            </servlet>
            <servlet-mapping>
                <cofaxCDS>/</cofaxCDS>
                <cofaxEmail>/cofaxutil/aemail/*</cofaxEmail>
                <cofaxAdmin>/admin/*</cofaxAdmin>
                <fileServlet>/static/*</fileServlet>
                <cofaxTools>/tools/*</cofaxTools>
            </servlet-mapping>
            <taglib>
                <taglib-uri>cofax.tld</taglib-uri>
                <taglib-location>/WEB-INF/tlds/cofax.tld</taglib-location>
            </taglib>
        </web-app>
    </object>
</root>

Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
<xsl:output method="text" encoding="utf-8"/>
<xsl:param name="jsonString" select="{ }"/>  <!-- you have to decide how you will get the JSON string into the transform -->

<xsl:template match="/">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','root','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="$jsonString"/></xsl:call-template></xsl:with-param>
</xsl:call-template>
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/root','&amp;gt;')"/>
</xsl:template>

<xsl:template name="parseNextToken">
<xsl:param name="remaining" select="'"/>
<xsl:param name="operandStack" select="','"/>

<xsl:variable name="operandChar" select="substring($remaining,1,1)"/>

<xsl:choose>
<xsl:when test="string-length($remaining) = 0"></xsl:when>

<xsl:when test="$operandChar = '{'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','object','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="concat($operandChar,$operandStack)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '}'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/object','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="substring($operandStack,2)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '['">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','array','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="concat($operandChar,$operandStack)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = ']'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/array','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="substring($operandStack,2)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '&amp;quot;'">
<!-- if the top of the operand stack is a comma, then this must be name
     if the top of the operand stack is a [ or :, then this must be a string value -->
	<xsl:choose>
	<xsl:when test="substring($operandStack,1,1) = ',' or substring($operandStack,1,1) = '{'">  <!-- name -->
		<xsl:variable name="nameString" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
        <xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','nameNode name=&amp;quot;',$nameString,'&amp;quot;/&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="concat(':',$operandStack)"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring-after(substring($remaining,2),'&amp;quot;'),':')"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = '['">  <!-- value in an array -->
		<xsl:variable name="theValue" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
		<xsl:variable name="afterTheValue"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring($remaining,2),'&amp;quot;')"/></xsl:call-template></xsl:variable>
		<xsl:variable name="remainingAfterValue"><xsl:choose><xsl:when test="substring($afterTheValue,1,1) = ','"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($afterTheValue,2)"/></xsl:call-template></xsl:when>
		                                                     <xsl:otherwise><xsl:value-of select="$afterTheValue"/></xsl:otherwise>
		                                         </xsl:choose>
	    </xsl:variable> <!-- $remainingAfterValue is used to swallow the comma in a series of values within an array -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;arrayItem type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[',$theValue,']]&amp;gt;&amp;lt;/arrayItem&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/> <!-- keep the bracket as the operator -->
			<xsl:with-param name="remaining" select="$afterTheValue"/>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = ':'">  <!-- elementary value  -->
		<xsl:variable name="theValue" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[',$theValue,']]&amp;gt;&amp;lt;/value&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring($remaining,2),'&amp;quot;')"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	</xsl:choose>
</xsl:when>

<xsl:when test="$operandChar = ','">
	<xsl:variable name="nextOperandStack"><xsl:choose><xsl:when test="substring($operandStack,1,1) = '['"><xsl:value-of select="$operandStack"/></xsl:when>
	                                                  <xsl:otherwise><xsl:value-of select="concat(',',$operandStack)"/></xsl:otherwise>
	                                      </xsl:choose></xsl:variable>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$nextOperandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:when>

<xsl:when test="contains('0123456789.-',$operandChar)">
	<xsl:variable name="numberString"><xsl:call-template name="getNumberString"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:variable>
	<xsl:variable name="afterNumberString"><xsl:call-template name="getAfterNumberString"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:variable>
	<xsl:choose>
	<xsl:when test="substring($operandStack,1,1) = '['">  <!-- value in an array -->
		<xsl:variable name="remainingAfterValue"><xsl:choose><xsl:when test="substring($afterNumberString,1,1) = ','"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($afterNumberString,2)"/></xsl:call-template></xsl:when>
		                                                     <xsl:otherwise><xsl:value-of select="$afterNumberString"/></xsl:otherwise>
		                                         </xsl:choose>
	    </xsl:variable> <!-- $remainingAfterValue is used to swallow the comma in a series of values within an array -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;arrayItem type=&amp;quot;number&amp;quot;&gt;',$numberString,'&amp;lt;/arrayItem&gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/> <!-- keep the bracket as the operator -->
			<xsl:with-param name="remaining" select="$remainingAfterValue"/>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = ':'">  <!-- elementary value  -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;number&amp;quot;&amp;gt;',$numberString,'&amp;lt;/value&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="$afterNumberString"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	</xsl:choose> 
</xsl:when>

<xsl:when test="substring($remaining,1,4) = 'true' or substring($remaining,1,5) = 'false' or substring($remaining,1,4) = 'null'">
<xsl:variable name="keyword"><xsl:choose><xsl:when test="substring($remaining,1,4) = 'true'">true</xsl:when>
                                         <xsl:when test="substring($remaining,1,5) = 'false'">false</xsl:when>
										 <xsl:when test="substring($remaining,1,4) = 'null'">null</xsl:when></xsl:choose></xsl:variable>
	<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;',$keyword,'&amp;quot;&amp;gt;',$keyword,'&amp;lt;/value&amp;gt;')"/>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$operandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,string-length($keyword)+1)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:when>

<xsl:otherwise>
	<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;parseError&amp;gt;',substring($remaining,1,1),'&amp;lt;/parseError&amp;gt;')"/>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$operandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="stripLeadingWhitespace">
<xsl:param name="theString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:variable name="firstChar" select="substring($theString,1,1)"/>
	<xsl:variable name="remaining" select="substring($theString,2)"/>
	<xsl:choose>
	<xsl:when test="string-length(normalize-space($firstChar)) &amp;gt; 0"><xsl:value-of select="$theString"/></xsl:when>
	<xsl:otherwise><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>

<xsl:template name="getNumberString">
<xsl:param name="theString" select="'"/>
<xsl:param name="outString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:choose>
	<xsl:when test="contains('0123456789.eE-+',substring($theString,1,1))"><xsl:call-template name="getNumberString">
	                                                                       <xsl:with-param name="theString" select="substring($theString,2)"/>
																		   <xsl:with-param name="outString" select="concat($outString,substring($theString,1,1))"/>
																		   </xsl:call-template></xsl:when>
	<xsl:otherwise><xsl:value-of select="$outString"/></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$outString"/></xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="getAfterNumberString">
<xsl:param name="theString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:choose>
	<xsl:when test="contains('0123456789.eE-+',substring($theString,1,1))"><xsl:call-template name="getAfterNumberString">
	                                                                       <xsl:with-param name="theString" select="substring($theString,2)"/>
																		   </xsl:call-template></xsl:when>
	<xsl:otherwise><xsl:value-of select="$theString"/></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
<xsl:output indent="yes" method="xml" encoding="utf-8"/>

<xsl:template match="/">
<root><xsl:apply-templates select="*[local-name() != 'nameNode']"/></root>
</xsl:template>

<xsl:template match="value">
<xsl:variable name="nodeName"><xsl:call-template name="createNodeName"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:value-of select="."/></xsl:element>
</xsl:template>

<xsl:template match="arrayItem">
<xsl:variable name="candidateNodeName"><xsl:choose>
                                <xsl:when test="local-name(parent::array[1]/preceding-sibling::*[1]) = 'nameNode'"><xsl:value-of select="parent::array[1]/preceding-sibling::*[1]/@name"/></xsl:when>
                                <xsl:otherwise><xsl:value-of select="local-name(.)"/></xsl:otherwise>
                              </xsl:choose></xsl:variable>
<xsl:variable name="nodeName"><xsl:value-of select="concat(translate(substring($candidateNodeName,1,1),'0123456789','xxxxxxxxxx'),translate(substring($candidateNodeName,2),'_ ','-'),'Item')"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:value-of select="."/></xsl:element>
</xsl:template>

<xsl:template match="object|array">
<xsl:variable name="nodeName"><xsl:call-template name="createNodeName"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:apply-templates select="*[local-name() != 'nameNode']"/></xsl:element>
</xsl:template>

<xsl:template name="createNodeName">
<xsl:variable name="candidateNodeName"><xsl:choose>
                                          <xsl:when test="local-name(preceding-sibling::*[1]) = 'nameNode'"><xsl:value-of select="preceding-sibling::*[1]/@name"/></xsl:when>
                                          <xsl:otherwise><xsl:value-of select="local-name(.)"/></xsl:otherwise>
                                       </xsl:choose></xsl:variable>
<!-- apply any adjustments necessary to get the JSON name to a valid name for an XML node.  
     As an example the following will ensure that the name will not begin with a digit or a space,
	 and that underscore and colon are converted to hyphen. -->
<xsl:variable name="actualNodeName"><xsl:value-of select="concat(translate(substring($candidateNodeName,1,1),'0123456789 ','xxxxxxxxxx'),translate(substring($candidateNodeName,2),':_ ','--'))"/></xsl:variable>
<xsl:value-of select="$actualNodeName"/>
</xsl:template>

</xsl:stylesheet>

Please let me know if you find this useful.



Tom Morrison
Micro Focus
 
It has been brought to my attention that the XSLT code was clipped. I am reposting the two XSLT.
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
<xsl:output method="text" encoding="utf-8"/>

<xsl:template match="/">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','root','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="/json-string"/></xsl:call-template></xsl:with-param>
</xsl:call-template>
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/root','&amp;gt;')"/>
</xsl:template>

<xsl:template name="parseNextToken">
<xsl:param name="remaining" select="'"/>
<xsl:param name="operandStack" select="','"/>

<xsl:variable name="operandChar" select="substring($remaining,1,1)"/>

<xsl:choose>
<xsl:when test="string-length($remaining) = 0"></xsl:when>

<xsl:when test="$operandChar = '{'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','object','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="concat($operandChar,$operandStack)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '}'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/object','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="substring($operandStack,2)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '['">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','array','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="concat($operandChar,$operandStack)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = ']'">
<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','/array','&amp;gt;')"/>
<xsl:call-template name="parseNextToken">
	<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	<xsl:with-param name="operandStack" select="substring($operandStack,2)"/>
</xsl:call-template>
</xsl:when>

<xsl:when test="$operandChar = '&amp;quot;'">
<!-- if the top of the operand stack is a comma, then this must be name
     if the top of the operand stack is a [ or :, then this must be a string value -->
	<xsl:choose>
	<xsl:when test="substring($operandStack,1,1) = ',' or substring($operandStack,1,1) = '{'">  <!-- name -->
		<xsl:variable name="nameString" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
        <xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;','nameNode name=&amp;quot;',$nameString,'&amp;quot;/&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="concat(':',$operandStack)"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring-after(substring($remaining,2),'&amp;quot;'),':')"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = '['">  <!-- value in an array -->
		<xsl:variable name="theValue" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
		<xsl:variable name="afterTheValue"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring($remaining,2),'&amp;quot;')"/></xsl:call-template></xsl:variable>
		<xsl:variable name="remainingAfterValue"><xsl:choose><xsl:when test="substring($afterTheValue,1,1) = ','"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($afterTheValue,2)"/></xsl:call-template></xsl:when>
		                                                     <xsl:otherwise><xsl:value-of select="$afterTheValue"/></xsl:otherwise>
		                                         </xsl:choose>
	    </xsl:variable> <!-- $remainingAfterValue is used to swallow the comma in a series of values within an array -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;arrayItem type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[',$theValue,']]&amp;gt;&amp;lt;/arrayItem&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/> <!-- keep the bracket as the operator -->
			<xsl:with-param name="remaining" select="$afterTheValue"/>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = ':'">  <!-- elementary value  -->
		<xsl:variable name="theValue" select="substring-before(substring($remaining,2),'&amp;quot;')"/>
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[',$theValue,']]&amp;gt;&amp;lt;/value&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring-after(substring($remaining,2),'&amp;quot;')"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	</xsl:choose>
</xsl:when>

<xsl:when test="$operandChar = ','">
	<xsl:variable name="nextOperandStack"><xsl:choose><xsl:when test="substring($operandStack,1,1) = '['"><xsl:value-of select="$operandStack"/></xsl:when>
	                                                  <xsl:otherwise><xsl:value-of select="concat(',',$operandStack)"/></xsl:otherwise>
	                                      </xsl:choose></xsl:variable>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$nextOperandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:when>

<xsl:when test="contains('0123456789.-',$operandChar)">
	<xsl:variable name="numberString"><xsl:call-template name="getNumberString"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:variable>
	<xsl:variable name="afterNumberString"><xsl:call-template name="getAfterNumberString"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:variable>
	<xsl:choose>
	<xsl:when test="substring($operandStack,1,1) = '['">  <!-- value in an array -->
		<xsl:variable name="remainingAfterValue"><xsl:choose><xsl:when test="substring($afterNumberString,1,1) = ','"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($afterNumberString,2)"/></xsl:call-template></xsl:when>
		                                                     <xsl:otherwise><xsl:value-of select="$afterNumberString"/></xsl:otherwise>
		                                         </xsl:choose>
	    </xsl:variable> <!-- $remainingAfterValue is used to swallow the comma in a series of values within an array -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;arrayItem type=&amp;quot;number&amp;quot;&amp;gt;',$numberString,'&amp;lt;/arrayItem&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/> <!-- keep the bracket as the operator -->
			<xsl:with-param name="remaining" select="$remainingAfterValue"/>
		</xsl:call-template>
	</xsl:when>
	<xsl:when test="substring($operandStack,1,1) = ':'">  <!-- elementary value  -->
		<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;number&amp;quot;&amp;gt;',$numberString,'&amp;lt;/value&amp;gt;')"/>
		<xsl:call-template name="parseNextToken">
			<xsl:with-param name="operandStack" select="$operandStack"/>
			<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="$afterNumberString"/></xsl:call-template></xsl:with-param>
		</xsl:call-template>
	</xsl:when>
	</xsl:choose> 
</xsl:when>

<xsl:when test="substring($remaining,1,4) = 'true' or substring($remaining,1,5) = 'false' or substring($remaining,1,4) = 'null'">
<xsl:variable name="keyword"><xsl:choose><xsl:when test="substring($remaining,1,4) = 'true'">true</xsl:when>
                                         <xsl:when test="substring($remaining,1,5) = 'false'">false</xsl:when>
										 <xsl:when test="substring($remaining,1,4) = 'null'">null</xsl:when></xsl:choose></xsl:variable>
	<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;value type=&amp;quot;',$keyword,'&amp;quot;&amp;gt;',$keyword,'&amp;lt;/value&amp;gt;')"/>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$operandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,string-length($keyword)+1)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:when>

<xsl:otherwise>
	<xsl:value-of disable-output-escaping="yes" select="concat('&amp;lt;parseError&amp;gt;',substring($remaining,1,1),'&amp;lt;/parseError&amp;gt;')"/>
	<xsl:call-template name="parseNextToken">
		<xsl:with-param name="operandStack" select="$operandStack"/>
		<xsl:with-param name="remaining"><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="substring($remaining,2)"/></xsl:call-template></xsl:with-param>
	</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="stripLeadingWhitespace">
<xsl:param name="theString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:variable name="firstChar" select="substring($theString,1,1)"/>
	<xsl:variable name="remaining" select="substring($theString,2)"/>
	<xsl:choose>
	<xsl:when test="string-length(normalize-space($firstChar)) &amp;gt; 0"><xsl:value-of select="$theString"/></xsl:when>
	<xsl:otherwise><xsl:call-template name="stripLeadingWhitespace"><xsl:with-param name="theString" select="$remaining"/></xsl:call-template></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>

<xsl:template name="getNumberString">
<xsl:param name="theString" select="'"/>
<xsl:param name="outString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:choose>
	<xsl:when test="contains('0123456789.eE-+',substring($theString,1,1))"><xsl:call-template name="getNumberString">
	                                                                       <xsl:with-param name="theString" select="substring($theString,2)"/>
																		   <xsl:with-param name="outString" select="concat($outString,substring($theString,1,1))"/>
																		   </xsl:call-template></xsl:when>
	<xsl:otherwise><xsl:value-of select="$outString"/></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$outString"/></xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template name="getAfterNumberString">
<xsl:param name="theString" select="'"/>
<xsl:choose>
<xsl:when test="string-length($theString) &amp;gt; 0">
	<xsl:choose>
	<xsl:when test="contains('0123456789.eE-+',substring($theString,1,1))"><xsl:call-template name="getAfterNumberString">
	                                                                       <xsl:with-param name="theString" select="substring($theString,2)"/>
																		   </xsl:call-template></xsl:when>
	<xsl:otherwise><xsl:value-of select="$theString"/></xsl:otherwise>
	</xsl:choose>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

XSLT for step 2 is in next submission.

Tom Morrison
Hill Country Software
 
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
<xsl:output indent="yes" method="xml" encoding="utf-8"/>

<xsl:template match="/">
<root><xsl:apply-templates select="*[local-name() != 'nameNode']"/></root>
</xsl:template>

<xsl:template match="value">
<xsl:variable name="nodeName"><xsl:call-template name="createNodeName"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:value-of select="."/></xsl:element>
</xsl:template>

<xsl:template match="arrayItem">
<xsl:variable name="candidateNodeName"><xsl:choose>
                                <xsl:when test="local-name(parent::array[1]/preceding-sibling::*[1]) = 'nameNode'"><xsl:value-of select="parent::array[1]/preceding-sibling::*[1]/@name"/></xsl:when>
                                <xsl:otherwise><xsl:value-of select="local-name(.)"/></xsl:otherwise>
                              </xsl:choose></xsl:variable>
<xsl:variable name="nodeName"><xsl:value-of select="concat(translate(substring($candidateNodeName,1,1),'0123456789','xxxxxxxxxx'),translate(substring($candidateNodeName,2),'_ ','-'),'Item')"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:value-of select="."/></xsl:element>
</xsl:template>

<xsl:template match="object|array">
<xsl:variable name="nodeName"><xsl:call-template name="createNodeName"/></xsl:variable>
<xsl:element name="{$nodeName}"><xsl:apply-templates select="*[local-name() != 'nameNode']"/></xsl:element>
</xsl:template>

<xsl:template name="createNodeName">
<xsl:variable name="candidateNodeName"><xsl:choose>
                                          <xsl:when test="local-name(preceding-sibling::*[1]) = 'nameNode'"><xsl:value-of select="preceding-sibling::*[1]/@name"/></xsl:when>
                                          <xsl:otherwise><xsl:value-of select="local-name(.)"/></xsl:otherwise>
                                       </xsl:choose></xsl:variable>
<!-- apply any adjustments necessary to get the JSON name to a valid name for an XML node.  
     As an example the following will ensure that the name will not begin with a digit or a space,
	 and that underscore and colon are converted to hyphen. -->
<xsl:variable name="actualNodeName"><xsl:value-of select="concat(translate(substring($candidateNodeName,1,1),'0123456789 ','xxxxxxxxxx'),translate(substring($candidateNodeName,2),':_ ','--'))"/></xsl:variable>
<xsl:value-of select="$actualNodeName"/>
</xsl:template>

</xsl:stylesheet>
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top