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.
Related
I need to transform a json message to XML. I have created a basic XSL transform script but the resulting XML uses 'map' tags with the json values as 'key' attributes.
Is there a way to have the name values used as tags or do I have to write a second transform XSL to get what I want?
json:
<?xml version="1.0"?>
<data>
{ "Policies":
{
"Policy": {
"PolicyNum": "1234",
"Customer": "Smith"
},
"Policy": {
"PolicyNum": "5678",
"Customer": "Jones"
}
}
}
</data>
xsl:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs math" version="3.0">
<xsl:output indent="yes" omit-xml-declaration="no" />
<xsl:template match="data">
<xsl:copy-of select="json-to-xml(.)"/>
</xsl:template>
</xsl:stylesheet>
resulting XML: (using https://xslttest.appspot.com/)
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
<map key="Policies">
<map key="Policy">
<string key="PolicyNum">1234</string>
<string key="Customer">Smith</string>
</map>
<map key="Policy">
<string key="PolicyNum">5678</string>
<string key="Customer">Jones</string>
</map>
</map>
</map>
The XML I need:
<Policies>
<Policy>
<PolicyNum>1234</PolicyNum>
<Customer>Smith</Customer>
</Policy>
<Policy>
<PolicyNum>5678</PolicyNum>
<Customer>Jones</Customer>
</Policy>
</Policies>
Instead of copying the XML map, push it through templates and transform that map into elements using the #key for the name:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs math" version="3.0">
<xsl:output indent="yes" omit-xml-declaration="no" />
<xsl:template match="data">
<xsl:apply-templates select="json-to-xml(.)"/>
</xsl:template>
<xsl:template match="fn:*[#key]">
<xsl:element name="{#key}">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="fn:map">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
I am trying to parse nested JSON to CSV, using XSLT transformation.
In this particular case each child object counting from "datasheet", e.g. "result-sheet" and "balance-sheet", should end up in one CSV file (output) each. Currently I am however just elaborating getting out "result-sheet" only.
I noticed that the content of arrays are getting merged togehter.
Data:
<data>
{
"datasheets": {
"result-sheet": {"bank": [1,3], "credit": [2,6]},
"balance-sheet": {"loans": [4,5], "inventory": [9,0]}
}
}
</data>
XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
>
<xsl:output method="text" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<xsl:apply-templates select="json-to-xml(.)"/>
</xsl:template>
<xsl:template match="*">
<h2>Result sheet</h2>
<xsl:text>
</xsl:text>
<xsl:value-of select="*/(string-join(head(*)/*/#key, ','), *!string-join(*, ','))" separator="
"/>
</xsl:template>
</xsl:stylesheet>
Result:
Result sheet
bank,credit
13,26
45,90
Wanted result:
bank,credit
1, 2,
3, 6
I don't quite understand which data you want to have in each line, the following templates creates a line using for-each-pair on each pair of fn:number elements in the two fn:array children of the fn:map with the #key being result-sheet:
<xsl:template match="*:map[#key = 'result-sheet']">
<xsl:value-of select="for-each-pair(*:array[1]/*:number, *:array[2]/*:number, function($n1, $n2) { $n1 || ', ' || $n2 })"
separator="
"/>
</xsl:template>
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>
Currently I need to convert json to xml and xml to json vice versa using XSLT 3.0 & Saxon-HE.
Below is my json abc.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<data>{
"cars" : [
{"doors" : "4","price" : "6L"},
{"doors" : "5","price" : "13L"}
]
}
</data>
</root>
Below is xsl file xyz.xsl
<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:output indent="yes"/>
<xsl:template match="data">
<xsl:copy-of select="json-to-xml(.)"/>
</xsl:template>
Below is output xml
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
<array key="cars">
<map>
<string key="doors">4</string>
<string key="price">6L</string>
</map>
<map>
<string key="doors">5</string>
<string key="price">13L</string>
</map>
</array>
</map>
Now My Question is how i can get back the same json from the output.xml? I am trying this using xslt function xml-to-json() but the output format is looking incorrect. Below is the xsl and output m getting.
123.xsl
<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:output indent="yes"/>
<xsl:template match="data">
<xsl:copy-of select="xml-to-json(.)"/>
</xsl:template>
</xsl:stylesheet>
Output JSon
Try this example here https://xsltfiddle.liberty-development.net/3NzcBsQ
In xsl I am selecting wrong template named data. because data template is not in output.xml. I am not sure what should i write here.
<xsl:template match="data">
You need to match on /, as in
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="xml-to-json(.)"/>
</xsl:template>
</xsl:stylesheet>
then the result is
{"cars":[{"doors":"4","price":"6L"},{"doors":"5","price":"13L"}]}
With
<xsl:template match="/">
<xsl:value-of select="xml-to-json(., map { 'indent' : true() })"/>
</xsl:template>
you get indentation although Saxon is not doing a nice job there:
{ "cars" :
[
{ "doors" : "4",
"price" : "6L" },
{ "doors" : "5",
"price" : "13L" } ] }
https://xsltfiddle.liberty-development.net/b4GWVd/1
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.