json to XML using XSL - json

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>

Related

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.

Remove namespace from serialized XML in XSLT

I am trying convert XML to Json with some transformation in XSLT 3. I have sample XML as shown below
Sample XML:
<Root>
<Employees xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Employee>
<name>abc</name>
</Employee>
<Employee>
<name>def</name>
</Employee>
<summary>
<Age>15</Age>
<tag1>dd</tag1>
<tag2>dd</tag2>
<tag2>dd</tag2>
</summary>
</Employees>
</Root>
My XSLT 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:strip-space elements="*"/>
<xsl:output method="json" indent="yes"/>
<xsl:template match="/Root">
<xsl:for-each select="Employees">
<xsl:sequence select="map { 'Root' :
array {
Employee[name!=''] ! map {
'Name':data(name),
'Date': format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01].[f0000001]Z'),
'Summary': 'Employee: ' || data(name) ||' Summary : ' || serialize(../summary)
}
}
}"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
{
"Root": [
{
"Summary":"Employee: abc Summary : <summary xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\"><Age>15<\/Age><tag1>dd<\/tag1><tag2>dd<\/tag2><tag2>dd<\/tag2><\/summary>",
"Name":"abc",
"Date":"2021-02-08T09:03:22.4740000Z"
},
{
"Summary":"Employee: def Summary : <summary xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\"><Age>15<\/Age><tag1>dd<\/tag1><tag2>dd<\/tag2><tag2>dd<\/tag2><\/summary>",
"Name":"def",
"Date":"2021-02-08T09:03:22.4740000Z"
}
]
}
Fiddle:https://xsltfiddle.liberty-development.net/6q1SDkM/5
I am facing issues where the summary node shows the namespaces in output. Looks like namespaces comes from XML tree which is mentioned in Employee node.How i can remove namespace.
Also in date formatting can we get fraction seconds upto 7 digits. Right now i am able to get only milliseconds
As I said in a comment to your previous question, you can push the element through a mode that explicitly doesn't copy namespaces:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output method="json" indent="yes"/>
<xsl:template match="/Root">
<xsl:for-each select="Employees">
<xsl:sequence select="map { 'Root' :
array {
Employee[name!=''] ! map {
'Name':data(name),
'Date': format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01].[f0000001]Z'),
'Summary': 'Employee: ' || data(name) ||' Summary : ' || ../summary => mf:strip-namespaces() => serialize()
}
}
}"/>
</xsl:for-each>
</xsl:template>
<xsl:mode name="strip-namespaces" on-no-match="shallow-copy"/>
<xsl:template mode="strip-namespaces" match="*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#* | node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:function name="mf:strip-namespaces" as="node()">
<xsl:param name="node" as="node()"/>
<xsl:apply-templates select="$node" mode="strip-namespaces"/>
</xsl:function>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6q1SDkM/6

How to select a node from an xml created by json-to-xml()

I am trying to get an element from a JSON after converting it to XML.
Working example: https://xsltfiddle.liberty-development.net/gWmuiJf/1
In this example, it is parsed successfully but when I want to select a node with the code below I could not bring the data I need.
<xsl:copy-of select="json-to-xml(root)//map[#key='identifier']"/>
JSON:
<root><![CDATA[{
"identifier": {
"use": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
}
}]]></root>
XSL:
<?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:template match="/">
<xsl:copy-of select="json-to-xml(root)"/>
</xsl:template>
</xsl:stylesheet>
My desired output is this:
<map key="identifier">
<string key="use"><div xmlns="http://www.w3.org/1999/xhtml"> </div></string>
</map>
or this
<string key="use"><div xmlns="http://www.w3.org/1999/xhtml"> </div></string>
The XML that is being generated by json-to-xml has a default namespace
<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>
Your XSLT is looking for a map in no namespace. So, you will have to adjust the xpath to cope with any namespace, but also use xsl:apply-templates so you can have a template that removes the namespace from the elements.
Try this XSLT
<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:mode on-no-match="shallow-copy"/>
<xsl:template match="/">
<xsl:apply-templates select="json-to-xml(root)//*:map[#key='identifier']"/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

xslt 3.0 json-to-xml and xml-to-json conversion

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

XML to JSON (Webhooks slack)

I would like to convert an XML to JSON using xslt transformation.The purpose is to POST into my slack channel using Incoming Webhooks.
XML File :
<?xml version="1.0" encoding="UTF-8" ?>
<Asset version="1.0">
<Process>
<Date>2017-01-24 14:47:35</Date>
<Status>Success</Status
> <Profile>TEST</Profile>
<Station>DESKTOP</Station>
<User>Système</User>
<Application>APP</Application>
</Process>
<Source>
</Source>
<Target>
<Name>Hello.mp4</Name>
</Target>
</Asset>
I tried this :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="txt" omit-xml-declaration="yes" encoding="UTF-8"/>
<xsl:template match="/">
{
"text":"<xsl:value-of select="Asset/Process/Profile"/>",
"text":"<xsl:value-of select="Asset/Process/Date"/>",
"text":"<xsl:value-of select="Asset/Target/Name"/>",
"text":"<xsl:value-of select="Asset/Process/Status"/>",
}
</xsl:template>
</xsl:stylesheet>
But i've got this error : 500 - missing_text_or_fallback_or_attachments
Do you have any idea ?
I need to have an JSON like this :
{ "text": "Date: 2017-01-24 14:47:35\n Status: Success\n Profile: TEST\n Station: DESKTOP\n User: Système\n Application: APP"}
https://api.slack.com/incoming-webhooks#sending_messages
Here is the XSLT which produces the desired JSON String:
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" omit-xml-declaration="yes" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:text>{ "text": "</xsl:text>
<xsl:for-each select="Asset/Process/*">
<xsl:choose>
<xsl:when test="position()=1">
<xsl:value-of select="concat(local-name(),': ',.,'\n')"/>
</xsl:when>
<xsl:when test="position()=last()">
<xsl:value-of select="concat(' ',local-name(),': ',.)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' ',local-name(),': ',.,'\n')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>"}</xsl:text>
</xsl:template>
</xsl:stylesheet>