Reputation: 341
I have the following xml
<Request>
<Record>
<ID>123456789</ID>
<OtherID>ABC123</OtherID>
<Title>Example</Title>
<Properties>
<Attribute type="Main">
<Name>Description</Name>
<Value>This is an example</Value>
</Attribute>
<Attribute type="Main">
<Name>Source</Name>
<Value>A1</Value>
</Attribute>
<Attribute type="Main">
<Name>Source</Name>
<Value>B</Value>
</Attribute>
<Attribute type="Main">
<Name>Represenative</Name>
<Value>Mike</Value>
</Attribute>
<Attribute type="Main">
<Name>Animal</Name>
<Value>Elephant</Value>
</Attribute>
</Properties>
</Record>
</Request>
I want to have the following json.
{
"Record":{
"ID":"123456789",
"OtherID":"ABC123",
"Title":"Example",
"Properties":[
{
"Type":"Main",
"Value":"Source",
"Name":"A1"
},
{
"Type":"Main",
"Value":"Source",
"Name":"B"
},
{
"Type":"Main",
"Value":"Representative",
"Name":"Mike"
},
{
"Type":"Main",
"Value":"Animal",
"Name":"Elephant"
}
],
"Description":"This is an example"
}
}
Please notice that the description property is not part of the array, it is at the same level as properties, ID, OtherID and Title.
I am applying the following xslt to transform the xml to json. In this xml I'm declaring a variable, which will contain the description and will be added at the end of the object
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:variable name="description2" select="''" />
{
<xsl:for-each select="Request/Record">
"Record": {
"ID":"<xsl:value-of select="ID" />",
"OtherId": "<xsl:value-of select="OtherID" />",
"Title":"Example",
<xsl:if test="Properties">
"Properties": [
<xsl:for-each select="Properties/Attribute">
<xsl:if test="soi:Name = 'Description'">
<xsl:variable name="description2" select="<xsl:value-of select="Value" />" />
<xsl:if test="position() != last()">,</xsl:if>
</xsl:if>
<xsl:if test="Name != 'Description'">
{
"Type": "Main",
"Name": "<xsl:value-of select="Name" />",
"Value": "<xsl:value-of select="Value" />"
}
<xsl:if test="position() != last()">,</xsl:if>
</xsl:if>
</xsl:for-each>
]
</xsl:if>
}<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:if test="description2!=''">
,"Description":"<xsl:value-of select="$description2"/>"
</xsl:if>
}
</xsl:template>
Unfortunately, I'm getting this output
{
"Request":{
"ID":"123456789",
"OtherID":"ABC123",
"Title":"Example",
"Properties":[
{
"Type":"Main",
"Value":"Source",
"Name":"A1"
},
{
"Type":"Main",
"Value":"Source",
"Name":"B"
},
{
"Type":"Main",
"Value":"Representative",
"Name":"Mike"
},
{
"Type":"Main",
"Value":"Animal",
"Name":"Elephant"
}
],
"Description":""
}
}
The value of description is empty because I do not know how to reassign the value of the variable description in the loop. I was using a double loop to get just the description, but it is very inefficient.
Any advise is welcomed
Thanks in advance.
Upvotes: 0
Views: 513
Reputation: 1
I've modified your original XSLT below, in order to populate the value for "Description".
I commented out the parts related to variable $description2. Instead of using a variable, I used XPath to select the element where [../Name='Description']
Good luck!
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/">
<!--<xsl:variable name="description2" select="''" />-->
{
<xsl:for-each select="Request/Record">
"Record": {
"ID":"<xsl:value-of select="ID" />",
"OtherId": "<xsl:value-of select="OtherID" />",
"Title":"Example",
<xsl:if test="Properties">
"Properties": [
<xsl:for-each select="Properties/Attribute">
<!--<xsl:if test="soi:Name = 'Description'">
<xsl:variable name="description2" select="<xsl:value-of select="Value" />" />
<xsl:if test="position() != last()">,</xsl:if>
</xsl:if>-->
<xsl:if test="Name != 'Description'">
{
"Type": "Main",
"Name": "<xsl:value-of select="Name" />",
"Value": "<xsl:value-of select="Value" />"
}
<xsl:if test="position() != last()">,</xsl:if>
</xsl:if>
</xsl:for-each>
]
</xsl:if>
}<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:if test="/Request/Record/Properties/Attribute/Value[../Name='Description']">
,"Description":"<xsl:value-of select="/Request/Record/Properties/Attribute/Value[../Name='Description']"/>"
</xsl:if>
}
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 167581
In XSLT 3 (as available with Saxon 9.8 or later and AltovaXML 2017 R3 and later) you can simply transform your XML to the XML format that the xml-to-json
function https://www.w3.org/TR/xpath-functions/#func-xml-to-json expects and then apply that function:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="Request">
<xsl:variable name="json-xml">
<map>
<xsl:apply-templates/>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="Record">
<map key="{local-name()}">
<xsl:apply-templates/>
</map>
</xsl:template>
<xsl:template match="ID | OtherID | Title">
<string key="{local-name()}">{.}</string>
</xsl:template>
<xsl:template match="Properties">
<array key="{local-name()}">
<xsl:apply-templates select="Attribute[not(Name = 'Description')]"/>
</array>
<string key="Description">{Attribute[Name = 'Description']/Value}</string>
</xsl:template>
<xsl:template match="Attribute">
<map>
<xsl:apply-templates select="@type, Value, Name"/>
</map>
</xsl:template>
<xsl:template match="Attribute/@* | Attribute/*">
<string key="{local-name()}">{.}</string>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bnnZWz/2
The same selection to distinguish Attribute[not(Name = 'Description')]
and Attribute[Name = 'Description']/Value
would of course also help with your original code.
Upvotes: 1