how to select specific node sets in xslt - xslt-1.0

The xslt I'm currently using generates all the tags on the root.
I need to get the <row> sets and <config> set.
Source Xml:
<root>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<items>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</items>
<tenders>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</tenders>
<taxes>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</taxes>
</root>
Attempted Xslt:
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="row/*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<root>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</root>

This short and simple transformation:
<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=
"node()[not(self::root or ancestor-or-self::config or ancestor-or-self::row)]">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<items>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</items>
<tenders>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</tenders>
<taxes>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</taxes>
</root>
produces the wanted, correct result:
<root>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</root>
Explanation:
Using and overriding the identity rule.
Proper use of the ancestor-or-self:: axis.

I figured it. This xslt works for me.
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each select="//row">
<row>
<xsl:apply-templates/>
</row>
</xsl:for-each>
<xsl:for-each select="//config">
<config>
<xsl:apply-templates/>
</config>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Related

Transfer multilevel element XML to one level

I have a xml with multiple items, which are Multilevel BOM (in the example 2 items, with both three levels). I need this to convert to xml with each record only the father and the Childs (first record of each item has no father).
We use XSLT 1.0 and we can't use Muenchian grouping because the processor in use don't know the key function.
I hope someone can help me out.
XML example:
<Items>
<level01>
<itemcode>L100</itemcode>
<quantity>1</quantity>
<whs>30</whs>
<level02>
<row>
<itemcode>L201</itemcode>
<quantity>5</quantity>
<whs>02</whs>
</row>
<row>
<itemcode>L202</itemcode>
<quantity>8</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>L203</itemcode>
<quantity>1</quantity>
<whs>01</whs>
<level03>
<row>
<itemcode>L301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>L302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
</level03>
</row>
</level02>
</level01>
<level01>
<itemcode>M100</itemcode>
<quantity>1</quantity>
<whs>30</whs>
<level02>
<row>
<itemcode>M201</itemcode>
<quantity>3</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M202</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M203</itemcode>
<quantity>2</quantity>
<whs>01</whs>
<level03>
<row>
<itemcode>M301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
<row>
<itemcode>M302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</row>
</level03>
</row>
</level02>
</level01>
</Items>
desired result:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<itemcode>L100</itemcode>
<quantity>1</quantity>
<whs>02</whs>
</Item>
<Item>
<father>L100</father>
<itemcode>L201</itemcode>
<quantity>5</quantity>
<whs>02</whs>
</Item>
<item>
<father>L100</father>
<itemcode>L202</itemcode>
<quantity>8</quantity>
<whs>01</whs>
</item>
<item>
<father>L100</father>
<itemcode>L203</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>L203</father>
<itemcode>L301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>L203</father>
<itemcode>L302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
</Items>
<Items>
<item>
<itemcode>M100</itemcode>
<quantity>1</quantity>
<whs>02</whs>
</item>
<item>
<father>M100</father>
<itemcode>M201</itemcode>
<quantity>3</quantity>
<whs>01</whs>
</item>
<item>
<father>M100</father>
<itemcode>M202</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</item>
<item>
<father>M100</father>
<itemcode>M203</itemcode>
<quantity>2</quantity>
<whs>01</whs>
</item>
<item>
<father>M203</father>
<itemcode>M301</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
<item>
<father>M203</father>
<itemcode>M302</itemcode>
<quantity>1</quantity>
<whs>01</whs>
</item>
</items>
<?bpc.pltype.out bpm.pltype=xml?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b1e="urn:com.sap.b1i.sim:b1event" xmlns:b1ie="urn:com.sap.b1i.sim:b1ievent" xmlns:b1im="urn:com.sap.b1i.sim:b1imessage" xmlns:bfa="urn:com.sap.b1i.bizprocessor:bizatoms" xmlns:exslt="http://exslt.org/common"
xmlns:jdbc="urn:com.sap.b1i.adapter:jdbcadapter" xmlns:js="com.sap.b1i.bpc_tools.Javascript" xmlns:rev="urn:com.sap.b1i.adapter:revaadapter" xmlns:rfc="urn:sap-com:document:sap:rfc:functions" xmlns:sim="urn:com.sap.b1i.sim:entity" xmlns:utils="com.sap.b1i.bpc_tools.Utilities"
xmlns:vpf="urn:com.sap.b1i.vplatform:entity" xmlns:xci="urn:com.sap.b1i.xcellerator:intdoc" version="1.0" exclude-result-prefixes="b1e b1ie b1im bfa jdbc js rfc utils xci vpf exslt sim rev" b1e:force="" b1ie:force="" b1im:force="" bfa:force="" jdbc:force=""
js:force="" rfc:force="" utils:force="" xci:force="" vpf:force="" exslt:force="" sim:force="" rev:force="">
<?prodver 1.0.0?>
<!--<xsl:include href="../../com.sap.b1i.dev.repository/IDE/init.xsl" />-->
<xsl:variable name="msg" select="/vpf:Msg/vpf:Body/vpf:Payload[./#Role='S']" />
<xsl:template match="/">
<Msg xmlns="urn:com.sap.b1i.vplatform:entity">
<xsl:copy-of select="/vpf:Msg/#*" />
<xsl:copy-of select="/vpf:Msg/vpf:Header" />
<Body>
<xsl:copy-of select="/vpf:Msg/vpf:Body/*" />
<Payload Role="X" id="999999">
<xsl:call-template name="transform" />
</Payload>
</Body>
</Msg>
</xsl:template>
<xsl:template name="transform">
This is the space we usually add our code
</xsl:template>
</xsl:stylesheet>
As I mentioned in the comments, I see no need for grouping here. The only complication is the irregularity of your input (no row wrapper at the top level) and of the output (no father element for the items with no parent). Otherwise this could be even simpler.
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:template match="/Items">
<xsl:copy>
<xsl:apply-templates select="level01"/>
</xsl:copy>
</xsl:template>
<xsl:template match="level01">
<Item>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</xsl:template>
<xsl:template match="row">
<Item>
<father>
<xsl:value-of select="(ancestor::*/itemcode)[last()]"/>
</father>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</xsl:template>
</xsl:stylesheet>
Do note that the output is somewhat different from the one in your question: the Items element is the root element of the entire output tree. Without this, you would receive an XML fragment instead of a well-formed XML document.
If you want an additional wrapper for each main branch, change the template matching level01 to:
<xsl:template match="level01">
<Branch>
<Item>
<xsl:copy-of select="itemcode|quantity|whs"/>
</Item>
<xsl:apply-templates select="*/row"/>
</Branch>
</xsl:template>

Grouping and select distinct in XSLT 1.0

I have an XML, i am trying to do kind of group by with XLST 1.0.
Input XMl:
<Rowset>
<Row>
<col1>7:00</col1>
<name>Shell Test</name>
<passCount>1</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>7:00</col1>
<name>Stroke Test</name>
<passCount>1</passCount>
<failCount>1</failCount>
</Row>
<Row>
<col1>7:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>1</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Shell Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Stroke Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>8:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Shell Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Stroke Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
<Row>
<col1>9:00</col1>
<name>Shutoff Test</name>
<passCount>0</passCount>
<failCount>0</failCount>
</Row>
</Rowset>
outPutXMl:
<?xml version="1.0" encoding="UTF-8"?>
<Row>
<element>
<TestName>Shell Test</TestName>
</element>
<element>
<TestName>Stroke Test</TestName>
</element>
<element>
<TestName>Shutoff Test</TestName>
</element>
</Row>
<Row>
<element>
<Time>7:00</Time>
<Pass>1</Pass>
<Fail>0</Fail>
<Pass>1</Pass>
<Fail>1</Fail>
<Pass>0</Pass>
<Fail>1</Fail>
</element>
<element>
<Time>8:00</Time>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
</element>
<element>
<Time>9:00</Time>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
<Pass>0</Pass>
<Fail>0</Fail>
</element>
</Row>
i am facing problem in extracting all the values from group in second for-each-group
my xslt 1.0 is as below:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="col1name" match="Row" use="name" />
<xsl:key name="time" match="Row" use="col1" />
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('col1name', name)[1])]">
<TestName><xsl:value-of select="name"/></TestName>
</xsl:for-each>
</Row>
<xsl:for-each select="Row">
<Time><xsl:value-of select="col1"/></Time>
<xsl:apply-templates select="/Rowsets/Rowset"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="/Rowsets/Rowset">
<xsl:for-each select="Row[generate-id() = generate-id(key('time', col1)[*])]">
<Pass><xsl:value-of select="passCount"/></Pass>
<Fail><xsl:value-of select="failCount"/></Fail>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
with XSLT 2.0 i could achieve this, but as per my requirement my application only supports XSLT 1.0
XSLT 2.0 Code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each-group select="Row" group-by="name">
<element>
<TestName>
<xsl:value-of select="name"/>
</TestName>
</element>
</xsl:for-each-group>
</Row>
<Row>
<xsl:for-each-group select="Row" group-by="col1">
<element>
<Time>
<xsl:value-of select="col1"/>
</Time>
<xsl:for-each select="current-group()">
<Pass>
<xsl:value-of select="passCount"/>
</Pass>
<Fail>
<xsl:value-of select="failCount"/>
</Fail>
</xsl:for-each>
</element>
</xsl:for-each-group>
</Row>
</xsl:template>
</xsl:stylesheet>
Can someone please help in replicating the output with XSLT 1.0
Use the two keys for Muenchian grouping:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="by-name" match="Row" use="name"/>
<xsl:key name="by-col1" match="Row" use="col1"/>
<xsl:template match="/Rowsets/Rowset">
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('by-name', name)[1])]">
<element>
<TestName>
<xsl:value-of select="name"/>
</TestName>
</element>
</xsl:for-each>
</Row>
<Row>
<xsl:for-each select="Row[generate-id() = generate-id(key('by-col1', col1)[1])]">
<element>
<Time>
<xsl:value-of select="col1"/>
</Time>
<xsl:for-each select="key('by-col1', col1)">
<Pass>
<xsl:value-of select="passCount"/>
</Pass>
<Fail>
<xsl:value-of select="failCount"/>
</Fail>
</xsl:for-each>
</element>
</xsl:for-each>
</Row>
</xsl:template>
</xsl:stylesheet>

How to get sum of immediate next nodes using XSLT 1.0

I have this XML file, where I have these nodes:
<Rows>
<Row type="Comment">
<Amount>0.00</Amount>
</Row>
<Row type="Spec">
<Amount>10.00</Amount>
</Row>
<Row type="Spec">
<Amount>10.00</Amount>
</Row>
<Row type="Spec">
<Amount>10.00</Amount>
</Row>
<Row type="Comment">
<Amount>0.00</Amount>
</Row>
<Row type="Spec">
<Amount>20.00</Amount>
</Row>
<Row type="Spec">
<Amount>10.00</Amount>
</Row>
<Row type="Spec">
<Amount>20.00</Amount>
</Row>
</Rows>
The result should be:
COMMENT: 30
COMMENT: 50
These Spec rows will always come after Comment rows. I need to do the sum of those Spec rows which are coming after Comment rows.
I tried to use Preceeding and Following functions in XSLT 1.0 but it is not working:
<xsl:value-of select="sum(../Row[#type='Spec']/Amount][following-sibling::row[1][#type='comment']])"/>
Can someone please help?
I would suggest you try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="spec" match="Row[#type='Spec']" use="generate-id(preceding-sibling::Row[#type='Comment'][1])" />
<xsl:template match="Rows">
<xsl:for-each select="Row[#type='Comment']">
<xsl:text>COMMENT: </xsl:text>
<xsl:value-of select="sum(key('spec', generate-id())/Amount)"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Multiple groupings of XML nodes

I'm trying to group the input below by the destination and assortment values using muenchian-grouping which is new for me so I'm not sure how to do it properly. The input files will be much larger than this so performance is important.
<?xml version="1.0"?>
<ns0:Data xmlns:ns0="http://BizTalk_Projects.input">
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA969</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA972</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_SA980</assortment>
<quantity>20</quantity>
</transports>
<transports>
<destination>destination 2</destination>
<assortment>Volvo_GA960</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
</ns0:Data>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>961</id>
<totalQuantity>25</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>969</id>
<totalQuantity>15</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>972</id>
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>40</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>45</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Volvo_SA</name>
<row>
<type>sumPerAssortment</type>
<id>980</id>
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>20</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Nissan_GA</name>
<row>
<type>sumPerAssortment</type>
<id>963</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>960</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
</ns0:Destinations>
Note:
assortment number starting with 96 = region 1
assortment number starting with 97 = region 2
assortment number starting with 98 = region 3
Start of my XSLT:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
exclude-result-prefixes="msxsl var s0"
version="1.0"
xmlns:s0="http://BizTalk_Projects.input"
xmlns:ns0="http://BizTalk_Projects.output">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="destinationKey" match="transports" use="destination"/>
<xsl:template match="/">
<xsl:apply-templates select="/s0:Data" />
</xsl:template>
<xsl:template match="/s0:Data">
<ns0:Destinations>
<xsl:for-each select="transports[count(. | key('destinationKey',destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination/text()" />
</name>
<xsl:for-each select="key('destinationKey',destination)">
<assortment>
<name>
<xsl:value-of select="substring(assortment/text(),1,string-length(assortment)-3)" />
</name>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</ns0:Destinations>
</xsl:template>
</xsl:stylesheet>
With this code, I'm getting this output (duplicate rows, but correct assortments for each destination);
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_SA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
</Destination>
</ns0:Destinations>
Any suggestions on how I can solve this? Help is very appreciated!
It's difficult to see how exactly the output relates to the input. Try this as your starting point:
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="*"/>
<xsl:key name="transports-by-destination" match="transports" use="destination" />
<xsl:key name="transports-by-assortment" match="transports" use="concat(destination, '|', assortment)" />
<xsl:template match="/*">
<xsl:copy>
<!-- for each unique destination -->
<xsl:for-each select="transports[count(. | key('transports-by-destination', destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination"/>
</name>
<xsl:variable name="group" select="key('transports-by-destination', destination)" />
<!-- for each unique assortment in this destination -->
<xsl:for-each select="$group[count(. | key('transports-by-assortment', concat(destination, '|', assortment))[1]) = 1]">
<assortment>
<name>
<xsl:value-of select="assortment"/>
</name>
<!-- process this subgroup -->
<xsl:for-each select="key('transports-by-assortment', concat(destination, '|', assortment))" >
<row>
<!-- not sure what goes in here -->
<totalQuantity>
<xsl:value-of select="quantity"/>
</totalQuantity>
</row>
</xsl:for-each>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT - First Instance of Empty Node edit

I am trying to edit the first instance of an empty node(Status) in the following xml :
<?xml version='1.0' encoding='windows-1252'?>
<root>
<row>
<flowname>1</flowname>
<path>#[payload]</path>
<id>3</id>
<setMessage>4</setMessage>
<MockOne>5</MockOne>
<MockTwo>6</MockTwo>
<MockThree>7</MockThree>
<Assert></Assert>
<status>12</status>
</row>
<row>
<flowname>2</flowname>
<path>4</path>
<id>5</id>
<setMessage>6</setMessage>
<MockOne>7</MockOne>
<MockTwo>8</MockTwo>
<MockThree></MockThree>
<Assert></Assert>
<status></status>
</row>
<row>
<flowname>3</flowname>
<path>5</path>
<id>6</id>
<setMessage>7</setMessage>
<MockOne>8</MockOne>
<MockTwo>9</MockTwo>
<MockThree></MockThree>
<Assert>3</Assert>
<status></status>
</row>
</root>
What I want to achieve is for the xslt to find the first instance of the tag which is empty and edit it to say 123. I tried using the following XSLT, but it seems to be replacing every Empty Status tag and I need it only to do the first instance. Kindly suggest on what has to be changed
The XSLT as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row/status[1][not(text())][1]">
<status>123</status>
</xsl:template>
</xsl:stylesheet>
The output right now is as follows( Every empty Status tag is replaced instead of the first instance of an empty one)
<root>
<row>
<flowname>1</flowname>
<path>#[payload]</path>
<id>3</id>
<setmessage>4</setmessage>
<mockone>5</mockone>
<mocktwo>6</mocktwo>
<mockthree>7</mockthree>
<assert></assert>
<status>12</status>
</row>
<row>
<flowname>2</flowname>
<path>4</path>
<id>5</id>
<setmessage>6</setmessage>
<mockone>7</mockone>
<mocktwo>8</mocktwo>
<mockthree></mockthree>
<assert></assert>
<status>123</status>
</row>
<row>
<flowname>3</flowname>
<path>5</path>
<id>6</id>
<setmessage>7</setmessage>
<mockone>8</mockone>
<mocktwo>9</mocktwo>
<mockthree></mockthree>
<assert>3</assert>
<status>123</status>
</row>
</root>
Your Xpath expression is saying to update all row/status which is empty.
You need to change like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row[not(preceding-sibling::row[not(normalize-space(status))])]/status[1][not(text())][1]">
<status>123</status>
</xsl:template>
</xsl:stylesheet>