XSLT 3.0 json-to-xml() not working with valid json - json

Below json is a valid Json. But XSLT 3.0 transformation json-to-xml() not working and showing some error in json syntax.
{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}
What can I do to make it work. I think some thing related to escaping characters need to be done here. Any pointer on this would be great help.
Try execution of code at this location Fiddler

You are trying to put your JSON with XML into an XML input document, that is causing the problem with the XML parser trying to parse that input you have put into the fiddle, if you use a string parameter for the stylesheet, as done in https://xsltfiddle.liberty-development.net/gWmuiJf, you get
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output indent="yes"/>
<xsl:param name="json-input" as="xs:string"><![CDATA[{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}]]></xsl:param>
<xsl:template match="/">
<xsl:copy-of select="json-to-xml($json-input)"/>
</xsl:template>
</xsl:stylesheet>
and the output is
<map xmlns="http://www.w3.org/2005/xpath-functions">
<map key="identifier">
<string key="use"><div xmlns="http://www.w3.org/1999/xhtml"> </div></string>
</map>
</map>
You could also use the same CDATA escaping in the primary XML input, that is, use
<root><![CDATA[{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}]]></root>
as the XML input and then
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="json-to-xml(root)"/>
</xsl:template>
</xsl:stylesheet>
as the XSLT, as done in https://xsltfiddle.liberty-development.net/gWmuiJf/1, and you get the same result as above.

In the "fiddler" you point to, you have an XML file:
<data>{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}
</data>
The problem is that this is invalid XML. The XML parser sees a start tag <data>, followed by a text node, followed by a start tag <div xmlns=\, and complains because the first character after xmlns= must be " rather than \.
So you have XML nested within JSON nested within XML. When you nest XML within JSON you must escape " as \", which you have done; but when you nest JSON within XML, you must escape < as <, which you have not done. The simplest solution is probably to use a CDATA section:
<data><![CDATA[{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}
]]></data>

Related

Converting JSON to XML using XSLT without XML tag in JSON file

I need your help in converting JSON format into XML format using XSLT.
My JSON
{
"SARIETransferRq": {
"SARIETransferRqHeader": {
"CorporateID": "ABCD0001",
"CorpReferenceNumber": "4716936"
},
"SARIETransferRqBody": {
"AccountNumber": "02020202020",
"DebitCurrency": "SAR",
"Instructions2": "luaba",
"Instructions3": "fadjiugu",
"Instructions4": "rifdij",
"Description": "sample description for service fee",
"AMLPurposeCode": "BB"
}
},
"Signature": {
"SignatureValue": "sample"
}
}
Output XML:
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<SARIETransferRq>
<SARIETransferRqHeader>
<CorporateID>ABCD0001</CorporateID>
<CorpReferenceNumber>4716936</CorpReferenceNumber>
</SARIETransferRqHeader>
<SARIETransferRqBody>
<AccountNumber>02020202020</AccountNumber>
<DebitCurrency>SAR</DebitCurrency>
<Instructions2>luaba</Instructions2>
<Instructions3>fadjiugu</Instructions3>
<Instructions4>rifdij</Instructions4>
<Description>sample description for service fee</Description>
<AMLPurposeCode>BB</AMLPurposeCode>
</SARIETransferRqBody>
</SARIETransferRq>
<Signature>
<SignatureValue>sample</SignatureValue>
</Signature>
</root>
In most of JSON to XML conversion i see there is xml tag in json file but my required doesn't have this XML tag in json file.
Can anyone suggest what needs to be corrected here:
Assuming XSLT 3 where you start with a named template and pass in the JSON as a string parameter (you could use a file name/URI to the JSON file as well and make that param then <xsl:param name="json" select="unparsed-text('input.json')"/>)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<root>
<xsl:apply-templates select="json-to-xml($json)/*"/>
</root>
</xsl:template>
<xsl:template match="*[#key]">
<xsl:element name="{#key}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:param name="json" as="xs:string" expand-text="no">
{
"SARIETransferRq": {
"SARIETransferRqHeader": {
"CorporateID": "ABCD0001",
"CorpReferenceNumber": "4716936"
},
"SARIETransferRqBody": {
"AccountNumber": "02020202020",
"DebitCurrency": "SAR",
"Instructions2": "luaba",
"Instructions3": "fadjiugu",
"Instructions4": "rifdij",
"Description": "sample description for service fee",
"AMLPurposeCode": "BB"
}
},
"Signature": {
"SignatureValue": "sample"
}
}
</xsl:param>
</xsl:stylesheet>
Online sample with SaxonJS.

How to convert JSON to XML using XSLT?

I want to convert JSON to XML using XSLT. But not able to achieve the expected output.
Below is the JSON request:
{
"Store": [
{
"Book": "Cartoons",
"ID": "ABC"
}
]
}
The XSLT which I tried:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:emp="http://www.semanticalllc.com/ns/employees#"
xmlns:h="http://www.w3.org/1999/xhtml"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:j="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs math xd h emp"
version="3.0"
expand-text="yes">
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="json-to-xml(.)/*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#key]" xpath-default-namespace="http://www.w3.org/2005/xpath-functions">
<xsl:element name="{#key}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
But I'm getting empty Response.
You need to pass the JSON as a parameter or read it from a file, the input to your XSLT is either XML or you can start with a named template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="json" as="xs:string" expand-text="no">{
"Store": [
{
"Book": "Cartoons",
"ID": "ABC"
}
]
}</xsl:param>
<xsl:output indent="yes"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="json-to-xml($json)"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/93wniTJ
The result of the function for your sample is
<map xmlns="http://www.w3.org/2005/xpath-functions">
<array key="Store">
<map>
<string key="Book">Cartoons</string>
<string key="ID">ABC</string>
</map>
</array>
</map>
but of course you can run it through further templates to transform it to a different format.
The pattern match="/" matches a document node (the root of an XML tree). It won't match your input if the input is JSON.
XSLT 3.0 isn't actually that good at processing JSON using template rules: it can be done, but it isn't very convenient. It's usually more convenient to use functions. You can supply the JSON input as the value of an xsl:param and process it in code from your xsl:initial-template; or if you're feeling more ambitious you could actually initiate the XSLT processing by invoking a public xsl:function that takes the JSON input as a parameter.
The traditional match="/" entry to a stylesheet only works for the traditional use of XSLT to process an XML input document.

How do I use the "replace" function in XSLT 1.0 to remove special characters from my XML to get the correct JSON output?

XML I was given
<root>
<page>0</page>
<totalRecords>74</totalRecords>
<totalPages>1</totalPages>
<offset>0</offset>
<status>success</status>
<entityList>
<ENAME>
<ADAMS>
</ENAME>
<DNAME>&RESEARCH</DNAME>
<JOB>CLERK</JOB>
<EMPNO>7876</EMPNO>
<HIREDATE>1987-05-23 "00: 00 ":00.0</HIREDATE>
<LOC>DALLAS</LOC>
</entityList>
<entityList>
<ENAME>>ALLEN</ENAME>
<DNAME>&SALES</DNAME>
<JOB>SALESMAN</JOB>
<EMPNO>7499</EMPNO>
<HIREDATE>1981-02-20 00:00:00.0</HIREDATE>
<LOC>CHICAGO</LOC>
</entityList>
<entityList>
<ENAME>Abhi></ENAME>
<DNAME>&SALES</DNAME>
<JOB>PRU</JOB>
<EMPNO>7956</EMPNO>
<HIREDATE></HIREDATE>
<LOC>CHIC"AGO "</LOC>
</entityList>
</root>
MY XSLT block thus far
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="no" omit-xml-declaration="yes" />
<xsl:template match="root">
{
"page":<xsl:value-of select="//page"/>,
"totalRecords":<xsl:value-of select="//totalRecords"/>,
"totalPages":<xsl:value-of select="//totalPages"/>,
"offset":<xsl:value-of select="//offset"/>,
"status":"<xsl:value-of select="//status"/>",
"entityList":[<xsl:for-each select="entityList">
{
"ENAME":"<xsl:value-of select="translate(ENAME,'<,>','')"/>",
"DNAME":"<xsl:value-of select="replace(DNAME,'&','')"/>",
"JOB":"<xsl:value-of select="JOB"/>",
"EMPNO":"<xsl:value-of select="EMPNO"/>",
"HIREDATE":"<xsl:value-of select="replace(HIREDATE,'"','')"/>",
"LOC":"<xsl:value-of select="replace(LOC,'"','')"/>"
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
</xsl:stylesheet>
Do I use the replace function or do I use translate? On here I see people use variables but I am new to XSLT and am just learning everything.
I need to remove the '&', the '<>', and the quotes in the hire date along with all other special characters in the XML
Please assist with pointers and tips as you can. Thank you.
There is no replace() function in XSLT 1.0; it was introduced in 2.0.
You can remove special characters using translate(), for example translate(., '&', '').
But what do you really want to do? Why are you trying to remove these characters? It looks as if you're trying to output JSON, in which case the way to handle these characters correctly is simply to use <xsl:output method="text"/>.

Using XSLT to transform XML to JSON

I would like to use XSLT to transform some XML into JSON.
The XML looks like the following:
<DATA_DS>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20001</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 1</ITEMDESCRIPTION>
</G_1>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20002</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 2</ITEMDESCRIPTION>
</G_1>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20003</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 3</ITEMDESCRIPTION>
</G_1>
</DATA_DS>
I expect the JSON to look like the following:
[
{
"Item_Number":"20001",
"Item_Description":"Item Description 1"
},
{
"Item_Number":"20002",
"Item_Description":"Item Description 2"
},
{
"Item_Number":"20003",
"Item_Description":"Item Description 3"
}
]
What is the recommended way to do this?
I am considering two approaches:
Try using the fn:xml-to-json function, as defined at https://www.w3.org/TR/xpath-functions-31/#func-xml-to-json. But as I understand, the input XML must follow a specific format defined at: https://www.w3.org/TR/xpath-functions-31/schema-for-json.xsd. And I also need the field names in the output JSON to be specifically "Item_Number" and "Item_Description".
Manually code the bracket and brace characters, "[", "]", "{", and "}", along with the field names "Item_Number" and "Item_Description". Then use a standard function to list the values and ensure that any special characters are handled properly. For example, the "&" character should appear normally in the JSON output.
What is the recommended way to do this, or is there a better way that I have not considered?
I would take the first approach, but start with transforming the given input to the XML format expected by the xml-to-json() function. This could be something like:
XSLT 3.0
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/G_1">
<!-- CONVERT INPUT TO XML FOR JSON -->
<xsl:variable name="xml">
<array>
<xsl:for-each-group select="*" group-starting-with="ORGANIZATION_NAME">
<map>
<string key="Item_Number">
<xsl:value-of select="current-group()[self::ITEMNUMBER]"/>
</string>
<string key="Item_Description">
<xsl:value-of select="current-group()[self::ITEMDESCRIPTION]"/>
</string>
</map>
</xsl:for-each-group>
</array>
</xsl:variable>
<!-- OUTPUT -->
<xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFWR5DQ
For simple mappings like that you can also directly construct XPath 3.1 arrays and maps i.e. in this case an array of maps:
<xsl:template match="DATA_DS">
<xsl:sequence select="array { G_1 ! map { 'Item_Number' : string(ITEMNUMBER), 'Item_Description' : string(ITEMDESCRIPTION) } }"/>
</xsl:template>
Then serialize as JSON with <xsl:output method="json" indent="yes"/>: https://xsltfiddle.liberty-development.net/ejivdGS
The main disadvantage is that maps have no order so you can't control the order of the items in a map, for instance for that example and the used Saxon version Item_Description is output before Item_Number.
But in general transforming to the format for xml-to-json provides more flexibility and also allows you to control the order as the function preserves the order in the XML representation of JSON.
This is the result of taking the solution posted by michael.hor257k and applying it to my revised input XML:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/DATA_DS">
<!-- CONVERT INPUT TO XML FOR JSON -->
<xsl:variable name="xml">
<array>
<xsl:for-each select="G_1">
<map>
<string key="Item_Number">
<xsl:value-of select="ITEMNUMBER"/>
</string>
<string key="Item_Description">
<xsl:value-of select="ITEMDESCRIPTION"/>
</string>
</map>
</xsl:for-each>
</array>
</xsl:variable>
<!-- OUTPUT -->
<xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>
</xsl:stylesheet>

XSLT 3.0 iterate over JSON array wrapped in XML

I am processing various XML files with XSLT. In one XML I found a wrapped JSON list:
<list><![CDATA[[
{
"title": "Title 1",
"value": "Value 1",
"order": 1
},
{
"title": "Title 2",
"value": "Value 2",
"order": 2
}
]]]>
</list>
My problem is that I need to iterate over the list. For example:
<xsl:variable name="listVar">
<!-- do something with list -->
</xsl:variable>
<xsl:for-each select="$listVar">
<!-- do something with objects in list e.g. -->
<xsl:value-of select="title"/>
<xsl:value-of select="value"/>
</xsl:for-each>
How to do this with XSLT? I use XSLT 3.0 and Saxon engine, version 9.8 HE.
Considered solutions:
1.
Use parse-json function:
But then I cannot iterate over the result because of XPathException: "Required item type of the context item for the child axis is node(); supplied value (.) has item type array(function(*))" or "Maps cannot be atomized". I found that there are
functions that probably I should take into account like map:get, map:entry, but I've failed to use them in my case so far.
2.
Addidiotnal transform before the one mentioned above:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output method="xml" encoding="UTF-8" indent="no"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="list">
<list>
<xsl:copy-of select="json-to-xml(.)"/>
</list>
</xsl:template>
</xsl:stylesheet>
And then:
<xsl:variable name="listVar" select="list/array/map"/>
But it does not work - probably due to added namespace
<list>
<array xmlns="http://www.w3.org/2005/xpath-functions">
<map>
...
Your JSON structure when parsed with parse-json gives you on the XSLT/XPath side an array of maps and the most straightforward way to process the individual array items is with the ?* lookup operator, then you can use for-each or even apply-templates:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:template match="list">
<xsl:apply-templates select="parse-json(.)?*"/>
</xsl:template>
<xsl:template match=".[. instance of map(xs:string, item())]">
<xsl:value-of select="?title, ?value"/>
</xsl:template>
</xsl:stylesheet>
where to access the map values you can again use ?foo as shown above.
As for working with the XML returned by json-to-xml, it returns elements in the XPath function namespace so to select them, as with any other elements in a namespace you need to make sure you set up a namespace with e.g. xpath-default-namespace for the section you want to process elements from that namespace or you can use the namespace wild card e.g. *:array/*:map.