xslt replacing empty array with dummy - xslt

I have a JSON request like
{
"aaaa": []
}
First I need to check aaaa exist in my request payload, if exists like as above, I need to add a jsonObject with dummy attributes and values like:
{
"aaaa": [
{
"#c": "test"
"a": "99999",
"b": "test",
"c": "test"
}
],
If aaaa does not exist in my payload, I need to add it with its dummy attributes and values also. So, if the payload is {} , after xslt, it should be the same JSON as above. In my transformation, I tried to handle this problem in this way:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//jsonObject">
<xsl:copy>
<xsl:if test="//jsonObject/not(aaaa)">
<aaaa>
<xsl:attribute name="c">test</xsl:attribute>
<a>99999</a>
<b>test</b>
<c>test</c>
</aaaa>
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//jsonObject/aaaa">
<xsl:copy>
<xsl:attribute name="c">test</xsl:attribute>
<a>99999</a>
<b>test</b>
<c>test</c>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With this request:
{
"aaaa": []
}
I cannot see aaaa array and its dummy attributes after transformation.
Thanks for any advice!

From WSO2 documentation
JSON:
{"array":[]}
XML (JsonStreamBuilder):
<jsonObject></jsonObject>
XML (JsonBuilder):
<jsonObject>
<?xml-multiple array?>
</jsonObject>
When I run your transformation with both outputs, I get
<jsonObject>
<aaaa c="test">
<a>99999</a>
<b>test</b>
<c>test</c>
</aaaa>
</jsonObject>
And
<jsonObject>
<aaaa c="test">
<a>99999</a>
<b>test</b>
<c>test</c>
</aaaa>
<?xml-multiple array?>
</jsonObject>
Do note: I'm not saying that I endorse your stylesheet. I would be better using:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="jsonObject[not(aaaa)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<aaaa c="test">
<a>99999</a>
<b>test</b>
<c>test</c>
</aaaa>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT create new tag if exists else update tag if exists

Is there a way for me to update the label element to Selma if "LastName" exists and if the label LastName doesn't exist then add the "LastName" and "label" elements to the XML?
<xml>
<udfs>
<udf>
<desc>FirstName</desc>
<label>Sam</label>
</udf>
<udf>
<desc>LastName</desc>
<label>Selman</label>
</udf>
</udfs>
</xml>
Here's what I have right now:
<xsl:stylesheet>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="udf[desc='LastName']/fieldValue">
<xsl:value-of select="'Selma'"/>
</xsl:template>
<xsl:template match="udf[not(desc='LastName')]">
<desc>LastName</desc>
<label>Selma</label>
</xsl:template>
</xsl:stylesheet>
I think you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="udf[desc='LastName']/label">
<label>Selma</label>
</xsl:template>
<xsl:template match="udfs[not(udf/desc='LastName')]">
<xsl:copy>
<xsl:apply-templates/>
<udf>
<desc>LastName</desc>
<label>Selma</label>
</udf>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Filter out elements and replace element value in one step

I am trying to filter out elements, and rename element value, but I can't get it to work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="xml">
<xsl:copy>
<xsl:for-each select="product[matches(code, 'C17.*[^V]$')]">
<xsl:copy>
<xsl:copy-of select="#*|node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="title">
<xsl:copy>
<xsl:value-of select="replace(.,'Apple','Carrot')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Example input data:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<product>
<code>C17020</code>
<title>Apple</title>
</product>
<product>
<code>C1723V</code>
<title>Samsung</title>
</product>
</xml>
I want to leave <product>'s starting with C17, but not ending to V. I use C17.*[^V]$ regex for this. This part is working.
The problem is with renaming title function. If I add this step to a new XSLT with code:
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
at the begin, then it works.
What I am doing wrong here?
The problem is you are doing <xsl:copy-of select="#*|node()"/> in your template matching xml. This will copy the attributes and child nodes, buy will not apply any templates. So your template matching title is just not used.
You need to use xsl:apply-templates here, but also include the identity template (the template you mention using in your new XSLT code) which ensures code gets copied too
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xml">
<xsl:copy>
<xsl:for-each select="product[matches(code, 'C17.*[^V]$')]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="title">
<xsl:copy>
<xsl:value-of select="replace(.,'Apple','Carrot')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note you can actually simplify your XSLT. Rather than being explicit in what you want to copy, by using the identity template you can instead have templates to remove what you don't want to copy....
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="product[not(matches(code, 'C17.*[^V]$'))]" />
<xsl:template match="title">
<xsl:copy>
<xsl:value-of select="replace(.,'Apple','Carrot')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Another thing to note is that matches and replace is for XSLT 2.0 only.

Editing some tags when using XSLTcopy

Assume the following example XML :
<tag>
<subtag1>value1</subtag1>
<subtag2>value2</subtag2>
<subtag3>value3</subtag3>
<subtag4>value4</subtag4>
<subtag5>value5</subtg5>
</tag>
and I would like to get all the tag section, but make some changes, like:
<tag>
<subtag1>value1</subtag1>
<subtag2>value2</subtag2>
<subtag3>value3</subtag3>
<new-subtag4>value4</new-subtag4>
<new-subtag5 type="new">value5</new-subtg5>
</tag>
I tried the following script, but the result is not correct.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parent::subtag4">
<xsl:element name="new-subtag4">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="parent::subtag5">
<xsl:element name="new-subtag5" type="n">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
An XSLT that outputs the desired result is the following. It incorporates only two minor modifications concerning the attribute and the removal of the parent:: prefixes.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="subtag4">
<xsl:element name="new-subtag4">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="subtag5">
<xsl:element name="new-subtag5">
<xsl:attribute name="type">new</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
parent::subtag4 is not a valid match pattern. And you cannot add an attribute to xsl:element like that. Nor do you need to use xsl:element when the element's name is known.
Try instead:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="subtag4">
<new-subtag4>
<xsl:apply-templates/>
</new-subtag4>
</xsl:template>
<xsl:template match="subtag5">
<new-subtag5 type="new">
<xsl:apply-templates/>
</new-subtag5>
</xsl:template>
</xsl:stylesheet>

Xslt to translate one to another and adding a row

Say I have an XML like this. I want to introduce a <row3> element.
<create xmlns="urn:partner.com"
<objects xmlns:p0="urn:s.partner.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="p0:IED">
<row1 xmlns="urn:s.partner.com">BookingDataFromCastIron</row1>
<row2 xmlns="urn:s.partner.com">Csv</row2>
</objects>
</create>"
I am using the following XSLT. But it is giving the same XML as output. Am I missing anything?
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="objects">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:element name="type">IED</xsl:element>
</xsl:copy>
</xsl:template>
It is all to do with the namespaces, basically your template match above only hits object if there is no namespace in the source XML. However you do have a namespace set xmlns="urn:partner.com" so you need to use the following to remove the binding to the namespace: <xsl:template match="*:objects">.
I think you are looking for this, but it defiantly pushes you in the right direction:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*:objects">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:element name="row3" xmlns="urn:s.partner.com">IED</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output:
<create xmlns="urn:partner.com">
<objects xmlns:p0="urn:s.partner.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<row1 xmlns="urn:s.partner.com">BookingDataFromCastIron</row1>
<row2 xmlns="urn:s.partner.com">Csv</row2>
<row3 xmlns="urn:s.partner.com">IED</row3>
</objects>
</create>

Remove elements based on other element's value -- XSLT

I have a style-sheet that I am using to remove certain elements based on the value of an other element. However, it is not working ...
Sample Input XML
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
If Operation value is 'ABC' then remove Text and Status nodes from XML.
And gives the following output.
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Here is my style sheet that I am using but it is removing Text and Status nodes from all XMLs even when operation is not 'ABC'.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="ID" select="//Operation"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text | Status">
<xsl:if test ="$ID ='ABC'">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Thanks in Advance
How would I do the same when namespace is present like
<ns0:next type="Sale" xmlns:ns0="http://Test.Schemas.Inside_Sales">
Here is a complete XSLT transformation -- short and simple (no variables, no xsl:if, xsl:choose, xsl:when, xsl:otherwise):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[Operation='ABC']/Text | *[Operation='ABC']/Status"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
the wanted, correct result is produced:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Change your xsl:if as follows:
<xsl:if test="../Operation!='ABC'">
and you can get rid of xsl:variable.
A better pattern in XSLT than using <xsl:if> is to add new templates with match conditions:
<xsl:template match="(Text | Status)[../Operation != 'ABC']"/>
I found this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Model">
<xsl:choose>
<xsl:when test="Operation[text()!='ABC']">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="Year"/>
<xsl:apply-templates select="Operation"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>