I would like convert JSON which is part of XML to XML structure using XSLT1.0(reason is our aplication only supports XSLT1.0 version).
Could someone be able to check and let me know on the same. Thank you
XML(with JSON): Fom below example, under the code tag, there is JSON which to be converted.
<root>
<root>
<order>131as</order>
<type>se134</type>
<num>15643</num>
<curr>USD</curr>
<code>{"desc":"Testing123","city":"Dubai","auth":"author","store":[{"quantity":1,"amount":2.00},{"quantity":100,"amount":-8.33},{"quantity":300,"amount":-15.00}]}</code>
</root>
<root>
<order>231as</order>
<type>se134</type>
<num>15643</num>
<curr>AED</curr>
<code>{"desc":"testdesc","city":"SHarjah","auth":"Mohammad","amount":20.00,"shop":"test","store":[{"quantity":1,"amount":10.00},{"quantity":100,"amount":98.33},{"quantity":300,"amount":-1.00}]}</code>
</root>
<root>
<order>331as</order>
<type>se134</type>
<num>15643</num>
<curr>SAR</curr>
<code/>
</root>
<root>
<order>431as</order>
<type>se134</type>
<num>15643</num>
<curr>USD</curr>
<code/>
</root>
<root>
<order>531as</order>
<type>Tse134</type>
<num>15643</num>
<curr>AED</curr>
<code>{"desc":"testdesc","city":"Abudabhi","auth":"Mr.121","amount":10.00,"shop":"testa","store":[{"quantity":71,"amount":10.00},{"quantity":100,"amount":95.33},{"quantity":300,"amount":-8.00}]}</code>
</root>
Expected output :
<root>
<root>
<order>131as</order>
<type>se134</type>
<num>15643</num>
<curr>USD</curr>
<code>
<root>
<amount>10.0</amount>
<auth>Mr.121</auth>
<city>Abudabhi</city>
<desc>testdesc</desc>
<shop>testa</shop>
<store>
<element>
<amount>10.0</amount>
<quantity>71</quantity>
</element>
<element>
<amount>95.33</amount>
<quantity>100</quantity>
</element>
<element>
<amount>-8.0</amount>
<quantity>300</quantity>
</element>
</store>
</root>
</code>
</root>
<root>
<order>231as</order>
<type>se134</type>
<num>15643</num>
<curr>AED</curr>
<code>
<root>
<amount>20.0</amount>
<auth>Mohammad</auth>
<city>SHarjah</city>
<desc>testdesc</desc>
<shop>test</shop>
<store>
<element>
<amount>10.0</amount>
<quantity>1</quantity>
</element>
<element>
<amount>98.33</amount>
<quantity>100</quantity>
</element>
<element>
<amount>-1.0</amount>
<quantity>300</quantity>
</element>
</store>
</root>
</code>
</root>
<root>
<order>331as</order>
<type>se134</type>
<num>15643</num>
<curr>SAR</curr>
<code/>
</root>
<root>
<order>431as</order>
<type>se134</type>
<num>15643</num>
<curr>USD</curr>
<code/>
</root>
<root>
<order>531as</order>
<type>Tse134</type>
<num>15643</num>
<curr>AED</curr>
<code>
<root>
<amount>10.0</amount>
<auth>Mr.121</auth>
<city>Abudabhi</city>
<desc>testdesc</desc>
<shop>testa</shop>
<store>
<element>
<amount>10.0</amount>
<quantity>71</quantity>
</element>
<element>
<amount>95.33</amount>
<quantity>100</quantity>
</element>
<element>
<amount>-8.0</amount>
<quantity>300</quantity>
</element>
</store>
</root>
</code>
</root>
Tried using the below XSLT which i found in one of blog .
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx" xmlns:exsl="http://exslt.org/common" xmlns:so="http://stackoverflow.com/questions/13007280" exclude-result-prefixes="xsl xs json so exsl">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="quot" select="'"'"/>
<xsl:variable name="numbers" select="'0123456789'"/>
<xsl:variable name="booleans" select="'tf'"/>
<xsl:template match="/*">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($t1)/so:output/*" mode="copy-sans-namespace"/>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="copy-sans-namespace"/>
</xsl:element>
</xsl:template>
<xsl:template name="field">
<!-- Input like: "Open": "25.15" bla -->
<!-- output like: <so:output><Open>25.15</Open></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:variable name="field-name" select="substring-before(substring-after($json-in,$quot),$quot)"/>
<xsl:variable name="remainder" select="substring-after($json-in,':')"/>
<xsl:call-template name="value">
<xsl:with-param name="json-in" select="$remainder"/>
<xsl:with-param name="parent-ele" select="$field-name"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="fields">
<!-- Input like: "Open": "25.15" , "High": "25.15" } bla -->
<!-- output like: <so:output><Open>25.15</Open><High>25.15</High></so:output> <so:extra>} bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:variable name="n" select="normalize-space($json-in)"/>
<xsl:choose>
<xsl:when test="substring($n,1,1) = $quot">
<xsl:variable name="t1">
<xsl:call-template name="field">
<xsl:with-param name="json-in" select="$n"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) "/>
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)=','">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="substring-after($t2,',')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra>
<xsl:value-of select="$t2"/>
</so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*"/>
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra"/>
</xsl:when>
<xsl:when test="$n">
<so:extra>
<xsl:value-of select="$n"/>
</so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="object">
<!-- Input like: { X } bla -->
<!-- output like: <so:output>fields(X)</so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele" select="''"/>
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'{'))"/>
<xsl:variable name="t2">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="$t1"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3" select="normalize-space(substring-after( exsl:node-set($t2)/so:extra, '}'))"/>
<so:output>
<xsl:choose>
<xsl:when test="$parent-ele">
<xsl:element name="{$parent-ele}">
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()"/>
</xsl:otherwise>
</xsl:choose>
</so:output>
<xsl:if test="$t3">
<so:extra>
<xsl:value-of select="$t3"/>
</so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="objects">
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="n" select="normalize-space($json-in)"/>
<xsl:choose>
<xsl:when test="substring($n,1,1) = '{'">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$n"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) "/>
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t2"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($t2,1,1)=',' and substring(normalize-space(substring-after($t2,',')),1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="normalize-space(substring-after($t2,','))"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra>
<xsl:value-of select="$t2"/>
</so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*"/>
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra"/>
</xsl:when>
<xsl:when test="$n">
<so:extra>
<xsl:value-of select="$n"/>
</so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="array">
<!-- Input like: [ X1 X2 ] bla -->
<!-- output like: <so:output><Y>X1</Y><Y>X2</Y></so:output> <so:extra>}bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'['))"/>
<xsl:variable name="t2">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t1"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="contains(substring-before(exsl:node-set($t2)/so:extra,']'),',')">
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,','))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,']'))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="t4">
<xsl:element name="{$parent-ele}">
<xsl:for-each select="$t2/so:output/*[local-name(.)=$parent-ele]">
<xsl:variable name="self" select="."/>
<xsl:variable name="tempResult">
<xsl:element name="{concat($parent-ele,'_element')}">
<xsl:copy-of select="exsl:node-set($self/*)"/>
</xsl:element>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($tempResult)"/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:variable name="t5" select="exsl:node-set($t4)"/>
<so:output>
<xsl:copy-of select="$t5"/>
</so:output>
<xsl:if test="$t3">
<so:extra>
<xsl:value-of select="$t3"/>
</so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="value">
<!-- Input like either array, object or string -->
<!-- output like either array, object or string -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="first-letter" select="substring(normalize-space($json-in),1,1)"/>
<xsl:choose>
<xsl:when test="$first-letter='{'">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$json-in"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter='['">
<xsl:call-template name="array">
<xsl:with-param name="json-in" select="$json-in"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter=$quot">
<xsl:call-template name="string">
<xsl:with-param name="json-in" select="$json-in"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($numbers,$first-letter)">
<xsl:call-template name="number">
<xsl:with-param name="json-in" select="$json-in"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($booleans,$first-letter)">
<xsl:call-template name="boolean">
<xsl:with-param name="json-in" select="$json-in"/>
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<so:output>ERROR</so:output>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="string">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="value" select="substring-before(substring-after($json-in,$quot),$quot)"/>
<xsl:variable name="remainder" select="normalize-space(substring-after(substring-after($json-in,$quot),$quot))"/>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value"/>
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra>
<xsl:value-of select="$remainder"/>
</so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="number">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value"/>
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra>
<xsl:value-of select="$remainder"/>
</so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="boolean">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in"/>
<xsl:param name="parent-ele"/>
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value"/>
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra>
<xsl:value-of select="$remainder"/>
</so:extra>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
3
Answers
Here is a something you could use as your starting point (actually, it’s almost complete, you only have to fill in some blanks). This follows what I said in the comment to your question: it is tailored specifically to your JSON’s structure.
XSLT 1.0
Applied to your input example, this will return:
Result
Writing a correct and complete parser for JSON in pure XSLT 1.0 is a challenging task, but it can be done.
In fact it has been done: the Rex parser generator from Gunther Rademacher at https://bottlecaps.de/rex/ will generate an XSLT parser for any grammar, and JSON is one of the grammars that comes "out of the box".
Rex is an excellent piece of software that sadly suffers from appalling documentation (and from not being truly open source, though it is free to use).
Using XSLT 3 is possible on many platforms (using e.g. Saxon HE Java, Saxon HE .NET, SaxonC HE, SaxonJS) and in XSLT 3 you can use e.g.
json-to-xml
and custom templates to achieve the result:Sample .NET 7 console code to run the example is e.g.