XLT 1.0 Comparing nodes to a loop - xslt

I need to copy all nodes and then change one element according to the following conditions.
If all nodes ship are 0, then replace = Rejected
If all node qty are ship, then replace = Accepted
If otherwise, then replace = Changed
No error occurs with 0 values, but if the second option does not work.
Input XML
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>123</a>
<b>231</b>
<c>
<xxx>
<qty>5</qty>
<ship>5</ship>
</xxx>
<xxx>
<qty>8</qty>
<ship>8</ship>
</xxx>
<xxx>
<qty>13</qty>
<ship>13</ship>
</xxx>
<xxx>
<qty>10</qty>
<ship>10</ship>
</xxx>
</c>
</root>
My XSLT
<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="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/a">
<a>
<xsl:choose>
<xsl:when test="not(/root/c/xxx/ship != '0')">Rejected</xsl:when>
<xsl:when test="not(/root/c/xxx/ship != /root/c/xxx/qty)">Accepted</xsl:when>
<xsl:otherwise>Changed</xsl:otherwise>
</xsl:choose>
</a>
</xsl:template>
</xsl:stylesheet>
UPD.
I begged xml, because I didn't think that the result would be through predicates
Now the structure of the input file is like this and I already use predicates
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>123</a>
<b>231</b>
<c>
<xxx>
<yyy>
<what>1</what>
<qty>5</qty>
</yyy>
<yyy>
<what>2</what>
<qty>5</qty>
</yyy>
</xxx>
<xxx>
<yyy>
<what>1</what>
<qty>24</qty>
</yyy>
<yyy>
<what>2</what>
<qty>24</qty>
</yyy>
</xxx>
...
</c>
</root>

I suppose you want <xsl:when test="not(/root/c/xxx[ship != qty])">Accepted</xsl:when>.

Related

Extract XSLT tags without unnecessary namespaces with XSLT 1.0

Unfortunately, i'm a newbie to XLST. I couldn't find a matching solution for my problem. I have to transform a file with XSLT 1.0. Given file:
<?xml version="1.0" encoding="UTF-8"?>
<A:A xsi:schemaLocation="urn:A A.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:A="urn:A">
<A:B>
<C xmlns="urn:C">
<D>foo</D>
</C>
</A:B>
<A:B>
<C xmlns="urn:C">
<D>bar</D>
</C>
</A:B>
</A:A>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<C xmlns="urn:C">
<D>foo</D>
</C>
<C xmlns="urn:C">
<D>bar</D>
</C>
I am stuck to get the desired result. My transformer:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:A="urn:A">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:template match="A:B">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
produces
<?xml version="1.0" encoding="UTF-8"?>
<A:B xmlns:A="urn:A">
<C xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:C">
<D>foo</D>
</C>
</A:B>
<A:B xmlns:A="urn:A">
<C xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:C">
<D>bar</D>
</C>
</A:B>
Any ideas how to get rid of the A:B envelope?
The code at https://xsltfiddle.liberty-development.net/nbiE19Q does
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*/*/node()"/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
and that way processes only the grandgrandchildren and their descendants with a template that strips all namespaces but the one each element is in.

how to transform a part of a xml to a new xml without namespace?

I have trouble in transform part of a xml to a new xml without namespace.
Input xml is:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<node1 xmlns="http://a.com">
<ServiceData>
<b:test xmlns:b="http://b.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</node1>
and what I want is:
<a>
<c>
<ServiceData>
<b:test xmlns:b="http://b.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</c>
</a>
The format I want is with no namespace for ServiceData.
Any help is appreciated, thanks.
Added, I tried to use this xsl, but I can't remove "xmlns="http://a.com""
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:test="http://a.com" exclude-result-prefixes="test">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="/">
<a><c><ServiceData><xsl:copy-of select="//test:ServiceData/*"/></ServiceData></c></a>
</xsl:template>
</xsl:stylesheet>
The result I got is:
<a>
<c>
<ServiceData>
<b:test xmlns:b="http://b.com" xmlns="http://a.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</c>
</a>
How about:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://a.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/node1">
<a>
<c>
<ServiceData>
<xsl:copy-of select="ServiceData/*" copy-namespaces="no" />
</ServiceData>
</c>
</a>
</xsl:template>
</xsl:stylesheet>
Added:
I assumed you could use an XSLT 2.0 processor, because your stylesheet says version="2.0". If that's not true, then you cannot use xsl:copy-of; instead, you must reconstruct the elements with their original names and namespaces:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:test="http://a.com"
exclude-result-prefixes="test">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/test:node1">
<a>
<c>
<ServiceData>
<xsl:apply-templates select="test:ServiceData/*"/>
</ServiceData>
</c>
</a>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note:
A redundant namespace declaration should not make any difference to the receiving application. The results received here are semantically identical to the result you show in your question.

Splitting Multiline XML tag into multiple Nodes

I have a XML below, where new lines are added after each line at Note__c tag. I need to produce the XML by splitting them into multiple Note__c tags.
Input XML-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Note__c>01/15/2020
123456
DFGRTE766
6tgBFR</Note__c>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
Where Note__c contains multiple strings with new line added after each(except the last one)
Expected Output -
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c>123456</Note__c>
<Note__c>DFGRTE766</Note__c>
<Note__c>6tgBFR</Note__c>
</Notes>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
I have written this XSLT but it is missing few tags under the payload element -
<xsl:stylesheet version="2.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="snotification/data/payload">
<Notes>
<xsl:for-each select="tokenize(Note__c,'\n')">
<Note__c>
<xsl:value-of select="."/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
Output of this-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c> 123456</Note__c>
<Note__c> DFGRTE766</Note__c>
<Note__c> 6tgBFR</Note__c>
</Notes>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
not sure what is missing.
Thanks
Sugata
Change your XSLT to
<xsl:stylesheet version="2.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="snotification/data/payload/Note__c">
<Notes>
<xsl:for-each select="tokenize(.,'\n')">
<Note__c>
<xsl:value-of select="normalize-space(.)"/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

XML manipulation. 2 Input source XML

I have got 2 source to the XSLT, which needs to be mapped to the target. Have given below the source and desired output. The first source XML is in a collection which needs to be iterated to fetch the value.
Input Payload:
XML 1:
<ParticipentsCollection>
<Participents>
<Email>PM#y.com</Email>
<Role>PM</Role>
</Participents>
<Participents>
<Email>BM#y.com</Email>
<Role>BM</Role>
</Participents>
<Participents>
<Email>CM#y.com</Email>
<Role>CM</Role>
</Participents>
</ParticipentsCollection>
XML 2:
<Project>
<ID>1</ID>
<Name>XYZ</Name>
<Status>Req Gathering</Status>
</Project>
Desired Output:
<ProjectDetails>
<ID>1</ID>
<Name>XYZ</Name>
<Status>Req Gathering</Status>
<PM>PM#y.com</PM>
<BM>PM#y.com</BM>
<CM>>CM#y.com</CM>
</ProjectDetails>
If you are using XSLT 1.0 use:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="exslt msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="Doc2"><xsl:copy><xsl:copy-of select="document('Untitled2.xml')/Project"></xsl:copy-of></xsl:copy></xsl:param>
<xsl:template match="ParticipentsCollection">
<ProjectDetails>
<xsl:copy-of select="exslt:node-set($Doc2)/Project/*"/>
<xsl:for-each select="Participents">
<xsl:element name="{Role}"><xsl:value-of select="Email"/></xsl:element>
</xsl:for-each>
</ProjectDetails>
</xsl:template>
</xsl:stylesheet>
and if 2.0 use:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="Doc2"><xsl:copy><xsl:copy-of select="document('Untitled2.xml')/Project"></xsl:copy-of></xsl:copy></xsl:param>
<xsl:template match="ParticipentsCollection">
<ProjectDetails>
<xsl:copy-of select="$Doc2/Project/*"/>
<xsl:for-each select="Participents">
<xsl:element name="{Role}"><xsl:value-of select="Email"/></xsl:element>
</xsl:for-each>
</ProjectDetails>
</xsl:template>
</xsl:stylesheet>
I am running this XSLT on XML1 and keeping XML2 in $Doc2 param to get output:
<ProjectDetails>
<ID>1</ID>
<Name>XYZ</Name>
<Status>Req Gathering</Status>
<PM>PM#y.com</PM>
<BM>BM#y.com</BM>
<CM>CM#y.com</CM>
</ProjectDetails>

Grouping of same tags under an element in xslt

I am working XSLT where the source looks like this.
Source:
<Data>
<AB>all</AB>
<AB>all2</AB>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</Data>
Need to write XSLT to get output as
<Info>
<XXX>
<TTT value="all"/>
<TTT value="all2"/>
</XXX>
<!-- ....-->
<!-- ..to het all the elements.. -->
</Info>
I have to write xslt to match tag.
<xsl:template match="AB">
</xsl:template>
I can do it by matching Data tag.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:for-each select="AB">
<TTT>
<xsl:attribute name="value">
<xsl:value-of select="."/>
</xsl:attribute>
</TTT>
</xsl:for-each>
</XXX>
</info>
</xsl:template>
</xsl:stylesheet>
Can any one help me out how to do it by matching AB tag
<xsl:template match="AB">
</xsl:template>
Thank you.
I think you are asking how do you use xsl:apply-templates. If so, your XSLT would look like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
</xsl:stylesheet>
Do also note the use of Attribute Value Templates in the AB template to simplify the XSLT.
If you also require the other non-AB elements to be output unchanged, you would make use of the identity transform in your XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
<xsl:apply-templates select="node()[not(self::AB)]" />
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This would also match all non-AB elements, outputing following the XXX element. In your case, it would output the following:
<info>
<XXX>
<TTT value="all" />
<TTT value="all2" />
</XXX>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</info>
Of course, there is no reason you couldn't have other templates matching elements like CD or DE to transform those too.