How to extract this XML Objects with XSLT - json

I want to extract the JSONx Data from a XML File to transform in JSON. So i need to extract the json:object from this File
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/ envelope/">
<soapenv:Body>
<json:object xmlns:json="http://www.ibm.com/xmlns/prod/2009/
jsonx" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://www.datapower.com/ schemas/json jsonx.xsd">
<json:string name="Number">XXX</json :string>
<json:string name="name">XXX</json:string> <json:string name="Date">4/84</json:string> <json:string name="amount">123</json:string>
</json:object> </soapenv:Body>
</soapenv:Envelope>
the result should look like this
<json:object xmlns:json="http://www.ibm.com/xmlns/prod/2009/
jsonx" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://www.datapower.com/ schemas/json jsonx.xsd">
<json:string name="Number">XXX</json :string>
<json:string name="name">XXX</json:string> <json:string name="Date">4/84</json:string> <json:string name="amount">1234</json:string>
</json:object>

Assuming XSLT 2.0 you can use xsl:copy-of and copy-namespaces="no" to exclude the SOAP namespace:
<xsl:template match="/">
<xsl:copy-of select="//object" xpath-default-namespace="http://www.ibm.com/xmlns/prod/2009/jsonx" copy-namespaces="no"/>
</xsl:template>

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.

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>

WSO2 ESB / WSO2 EI JSON Request to backend service

I have sent JSON Message to backend server using WSO2 EI. But it sending wrong json format. I have used wso2ei-6.4.0 for this task. I have added sample XML request which I have used.
<jsonObject>
<checkInDate>2019-03-25</checkInDate>
<checkOutDate>2019-03-26</checkOutDate>
<cityCode>3086</cityCode>
<hotelCode />
<roomOccupancyList>
<jsonArray>
<?xml-multiple jsonElement?>
<jsonElement>
<childAges>0</childAges>
<numberOfAdults>1</numberOfAdults>
<numberOfChildren>0</numberOfChildren>
<roomNo>1</roomNo>
</jsonElement>
</jsonArray>
</roomOccupancyList>
</jsonObject>
But I got following JSON request from ESB side. Once send the message through the ESB service.
{
"checkInDate": "2019-06-04",
"checkOutDate": "2019-06-05",
"cityCode": "1344",
"hotelCode": "",
"roomOccupancyList": {
"childAges": "0",
"numberOfAdults": "1",
"numberOfChildren": "0",
"roomNo": "1"
}
}
This "roomOccupancyList" should have JSONArray Object, by default it showing as JSONObject. If I have add multiple jsonElement here, it showing as JSONArray. But I need even one jsonElement also. Can you please help me to fixed this issue. I have refereed following link for above task. wso2 doc link
JSON Response should load following format.
{
"checkInDate": "2019-06-04",
"checkOutDate": "2019-06-05",
"cityCode": "1344",
"hotelCode": "",
"roomOccupancyList": [
{
"childAges": "0",
"numberOfAdults": "1",
"numberOfChildren": "0",
"roomNo": "1"
}
]
}
I have used following xslt for this and it is working without any issue. Also you need to add following property in synapse.properties /ESB_HOME/conf folder.
synapse.json.to.xml.processing.instruction.enabled=true
This the sample XSLT for your further referance.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="/request">
<availabilityRequest>
<jsonObject>
<checkInDate>2019-03-25</checkInDate>
<checkOutDate>2019-03-26</checkOutDate>
<cityCode>3086</cityCode>
<xsl:variable name="roomCount" select="count(roomsInformation)" />
<xsl:for-each select="roomsInformation">
<xsl:if test="$roomCount = 1">
<xsl:processing-instruction name="xml-multiple"/>
</xsl:if>
<xsl:variable name="roomIndex" select="position()" />
<roomOccupancyList>
<jsonArray>
<childAges>0</childAges>
<numberOfAdults>1</numberOfAdults>
<numberOfChildren>1</numberOfChildren>
<roomNo><xsl:value-of select="$roomIndex" /></roomNo>
</jsonArray>
</roomOccupancyList>
<xsl:if test="$roomCount = 1">
<xsl:copy-of select="Objects"></xsl:copy-of>
</xsl:if>
</xsl:for-each>
</jsonObject>
</availabilityRequest>
</xsl:template>
</xsl:stylesheet>

XSLT 3.0 json-to-xml() not working with valid 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>

rest extension will not transform json

I have a MarkLogic Rest extension which needs to transform JSON into OBI objects. For starters I created an XML input and created transforms for it, therse work.
Now the real data is slightly different and in JSON, so I need to transform the JSON to XML. I have had an example xsl transform that I cannot seem to get to work...
The JSON doc that I want to load via the rest extension:
{
"wifi_raw": [
{
"id": "4354279",
"hostname": "rb-0046",
"mac": "00:0C:43:00:08:F4",
"firstseen": "2015-08-12 13:54:50",
"lastseen": "2015-08-12 13:54:50",
"rssi": "-1",
"packets": "1",
"bssid": "24:A4:3C:53:19:62",
"probes": "",
"processed": "0"
},
{
"id": "4354257",
"hostname": "rb-0046",
"mac": "00:0E:58:BC:E9:03",
"firstseen": "2015-08-12 13:48:45",
"lastseen": "2015-08-12 13:52:10",
"rssi": "-58",
"packets": "3",
"bssid": "B8:E9:37:17:DA:EF",
"probes": "sonos_hbbzdjrspta2htbcqeb0gcjouc",
"processed": "0"
},
{
"id": "4354273",
"hostname": "rb-0046",
"mac": "00:0E:58:BC:E9:03",
"firstseen": "2015-08-12 13:48:45",
"lastseen": "2015-08-12 13:54:32",
"rssi": "-61",
"packets": "4",
"bssid": "B8:E9:37:17:DA:EF",
"probes": "sonos_hbbzdjrspta2htbcqeb0gcjouc",
"processed": "0"
}
]
}
On ingest I want to transform this to OBI objects which are all defined and work for the equivalent XML input doc...
If I do
json:transform-from-json($source)
In the ingest extension the JSON transforms to this XML:
<?xml version="1.0"?>
<json xmlns="http://marklogic.com/xdmp/json/basic" type="object">
<wifi__raw type="array">
<json type="object">
<id type="string">4354279</id>
<hostname type="string">rb-0046</hostname>
<mac type="string">00:0C:43:00:08:F4</mac>
<firstseen type="string">2015-08-12 13:54:50</firstseen>
<lastseen type="string">2015-08-12 13:54:50</lastseen>
<rssi type="string">-1</rssi>
<packets type="string">1</packets>
<bssid type="string">24:A4:3C:53:19:62</bssid>
<probes type="string"/>
<processed type="string">0</processed>
</json>
<json type="object">
<id type="string">4354257</id>
<hostname type="string">rb-0046</hostname>
<mac type="string">00:0E:58:BC:E9:03</mac>
<firstseen type="string">2015-08-12 13:48:45</firstseen>
<lastseen type="string">2015-08-12 13:52:10</lastseen>
<rssi type="string">-58</rssi>
<packets type="string">3</packets>
<bssid type="string">B8:E9:37:17:DA:EF</bssid>
<probes type="string">sonos_hbbzdjrspta2htbcqeb0gcjouc</probes>
<processed type="string">0</processed>
</json>
<json type="object">
<id type="string">4354273</id>
<hostname type="string">rb-0046</hostname>
<mac type="string">00:0E:58:BC:E9:03</mac>
<firstseen type="string">2015-08-12 13:48:45</firstseen>
<lastseen type="string">2015-08-12 13:54:32</lastseen>
<rssi type="string">-61</rssi>
<packets type="string">4</packets>
<bssid type="string">B8:E9:37:17:DA:EF</bssid>
<probes type="string">sonos_hbbzdjrspta2htbcqeb0gcjouc</probes>
<processed type="string">0</processed>
</json>
</wifi__raw>
</json>
My minimum xsl transform now is this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xdmp="http://marklogic.com/xdmp"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:basic="http://marklogic.com/xdmp/json/basic"
xmlns:map="http://marklogic.com/xdmp/map"
xmlns:obj="http://marklogic.com/solutions/obi/object"
extension-element-prefixes="xdmp">
<xdmp:import-module namespace="http://marklogic.com/xdmp/json" href="/MarkLogic/json/json.xqy"/>
<xsl:param name="params" as="map:map"/>
<xsl:variable name="ingest-time" select="(map:get($params, 'ingest-time'), 'false')[1] = 'true'"/>
<xsl:template match="/text()[empty(../*)]">
<xsl:apply-templates select="json:transform-from-json(.)"/>
</xsl:template>
<xsl:template match="basic:json">
<results>
<objects>
<!-- test -->
<xsl:text>example</xsl:text>
</objects>
<links>
<!-- links go here-->
<xsl:text>example</xsl:text>
</links>
</results>
</xsl:template>
</xsl:stylesheet>
I get NO results back from the
let $result := eput:apply-document-transform(fn:concat($dataset, '-transform'), $params, $context, $source)/element()
In the ingest extension.
As I understood this the match "match="/text()[empty(../*)]">" would be true for the JSON document node, so the same transform-from-json would generate the XML version of the original JSON doc also posted here above so I could at least find the root node with "match="basic:json""
What am I missing here?
EDIT WORKING SOLUTION
This is basically what worked. First we made sure the new JSON object-node() is matched, then you have to be really carefull with the matching XSL templates. I had two templates matched for "basic:json" witch resulted in only the last to be executed...
Now the root is matched with
<xsl:template match="/basic:json">
And the one inside the array is matched with ANY child of wifi__raw like
<xsl:template match="basic:wifi__raw/*">
The complete XSL now looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xdmp="http://marklogic.com/xdmp"
xmlns:json="http://marklogic.com/xdmp/json"
xmlns:basic="http://marklogic.com/xdmp/json/basic"
xmlns:map="http://marklogic.com/xdmp/map"
xmlns:obj="http://marklogic.com/solutions/obi/object"
extension-element-prefixes="xdmp">
<xdmp:import-module namespace="http://marklogic.com/xdmp/json" href="/MarkLogic/json/json.xqy"/>
<xsl:param name="params" as="map:map"/>
<xsl:variable name="ingest-time" select="(map:get($params, 'ingest-time'), 'false')[1] = 'true'"/>
<!-- voor MarkLogic 7 -->
<xsl:template match="/text()[empty(../*)]">
<xsl:apply-templates select="json:transform-from-json(.)"/>
</xsl:template>
<!-- voor MarkLogic 8 -->
<xsl:template match="/node()[not(self::*)]">
<xsl:apply-templates select="json:transform-from-json(.)"/>
</xsl:template>
<xsl:template match="/basic:json">
<xsl:apply-templates select="basic:wifi__raw"/>
</xsl:template>
<xsl:template match="basic:wifi__raw">
<xsl:variable name="objects" as="element()*">
<xsl:apply-templates select="*" />
</xsl:variable>
<results>
<objects>
<xsl:sequence select="$objects" />
</objects>
</results>
</xsl:template>
<xsl:template match="basic:wifi__raw/*">
<foundId>
<xsl:value-of select="basic:id" />
</foundId>
</xsl:template>
</xsl:stylesheet>
This is caused by MarkLogic 8 handling JSON datatype differently from MarkLogic 7 and before. In MarkLogic 8, JSON data gets processed into object-node() data, not into text(). Unfortunately, you cannot explicitly test for object-node() in XSLT, but there are a few ways to work around that. Change the match for text() like this:
<xsl:template match="/node()[not(self::*)]">
<xsl:apply-templates select="json:transform-from-json(.)"/>
</xsl:template>
This should work in both MarkLogic 7 and 8.
HTH!