Grouping the immediate nodes in XSLT - xslt

I am using the XSLT 1.0 version and I have the below XML to transform,
<Root>
<Child No="1" Month="0" Date="13/08/2014" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="2" Month="1" Date="13/09/2014" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="3" Month="2" Date="13/10/2014" Payment="200">
<Totals amount="200"/>
</Child>
<Child No="4" Month="3" Date="13/11/2014" Payment="300">
<Totals amount="300"/>
</Child>
<Child No="5" Month="4" Date="13/12/2014" Payment="300">
<Totals amount="300"/>
</Child>
<Child No="6" Month="5" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="7" Month="6" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="8" Month="7" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="9" Month="8" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="10" Month="9" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
</Root>
I wanted to transform The XML as
<PPS>
<PP>
<Ps>
<StartMonth>0</StartMonth>
<EndMonth>1</EndMonth>
<P>
<Amount>100</Amount>
<startP>1</startP>
</P>
<P>
<Amount>100</Amount>
<startP>2</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>2</StartMonth>
<EndMonth>2</EndMonth>
<P>
<Amount>200</Amount>
<startP>3</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>3</StartMonth>
<EndMonth>4</EndMonth>
<P>
<Amount>300</Amount>
<startP>4</startP>
</P>
<P>
<Amount>300</Amount>
<startP>5</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>5</StartMonth>
<EndMonth>9</EndMonth>
<P>
<Amount>100</Amount>
<startP>6</startP>
</P>
<P>
<Amount>100</Amount>
<startP>7</startP>
</P>
<P>
<Amount>100</Amount>
<startP>8</startP>
</P>
<P>
<Amount>100</Amount>
<startP>9</startP>
</P>
<P>
<Amount>100</Amount>
<startP>10</startP>
</P>
</Ps>
</PP>
</PPS>
every time I wanted to create a "P" differing from the last Payment.
Here is the pseudo code, create a new PP, when child[n]/Payment <> child[n-1]/Payment.
P.S., I do not want to consider the Totals node even it has some values.
Many Thanks in advance.

You can use the following XSLT. It wouldn't have been so long if it was in XSLT2.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="Child">
<xsl:variable name="Payment" select="#Payment"/>
<xsl:variable name="start-end">
<StartMonth>
<xsl:value-of select="#Month"/>
</StartMonth>
<xsl:choose>
<xsl:when test="following-sibling::*[1][self::Child and #Payment = $Payment]">
<EndMonth>
<xsl:apply-templates select="following-sibling::*[1]" mode="month">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</EndMonth>
</xsl:when>
<xsl:otherwise>
<EndMonth>
<xsl:value-of select="#Month"/>
</EndMonth>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="not(preceding-sibling::*[1][self::Child and #Payment = $Payment])">
<PP>
<Ps>
<xsl:copy-of select="$start-end"/>
<xsl:apply-templates select="current()"/>
<xsl:apply-templates select="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</Ps>
</PP>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
<xsl:template match="Child">
<xsl:param name="Payment"/>
<P>
<Amount>
<xsl:value-of select="#Payment"/>
</Amount>
<startP>
<xsl:value-of select="#No"/>
</startP>
</P>
<xsl:apply-templates select="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Child" mode="month">
<xsl:param name="Payment"/>
<xsl:choose>
<xsl:when test="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:apply-templates select="following-sibling::*[1]" mode="month">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#Month"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Related

XSLT transform with templates: remove empty xmlns=""

How do I remove the empty xmlns="" in my transformation?
Below is the example of what shows up in the resulting XML. Notice the empty xmlns.
I'm working with VisualStudio 2019 16.8.4.
This is the source XML that I need to transform.
<?xml version="1.0" encoding="UTF-8"?>
<wPOREQ payloadID='113084912-000001' timestamp='2020-12-16T14:05:36-06:00' version='3.1'>
<Header mode='Production'>
<From>
<Credential domain='DUNS'>
<Identity>060480999</Identity>
</Credential>
</From>
<To>
<Credential domain='DUNS'>
<Identity>060480999</Identity>
</Credential>
</To>
<Sender>
<Credential domain='DUNS'>
<Identity>196488888</Identity>
<SharedSecret>111</SharedSecret>
</Credential>
</Sender>
</Header>
<Order deploymentMode='production' rush='N'>
<PONumber>59test</PONumber>
<ReferenceNumber>59240249</ReferenceNumber>
<WCSSCustomerId>0810999</WCSSCustomerId>
<OrderExtrinsic>
<Extrinsic name='PONUM'>MM000327999</Extrinsic>
</OrderExtrinsic>
<ShipTo>
<WCSSShipTo>0001</WCSSShipTo>
<LocationCode/>
<Name1>SHIP TO THIS</Name1>
<Name2/>
<Address1>99 MEDICAL CENTER DRIVE</Address1>
<Address2>HSC BMRC</Address2>
<Address3>BK TEST</Address3>
<City>ANYTOWN</City>
<State>WV</State>
<ZipCode>265053409</ZipCode>
<CountryCode>US</CountryCode>
<Phone>
<AreaCode/>
<PhNumber/>
<Ext/>
</Phone>
<Fax>
<AreaCode/>
<PhNumber/>
<Ext/>
</Fax>
<Attention>RM:8707</Attention>
<Email/>
</ShipTo>
<OrderDate>2020-12-16T14:05:36-06:00</OrderDate>
<DeliverByDate>2021-01-31</DeliverByDate>
<RequestedShipDate/>
<RequestedCarrier>UPSS</RequestedCarrier>
<ShippingMessage>CUST CARRIER ACCT: 021FW0</ShippingMessage>
<LineItems>
<LineItem>
<LineNumber>1</LineNumber>
<SKU>10037475</SKU>
<SKUDescription>WVU LETTERHEAD</SKUDescription>
<Quantity>1000</Quantity>
<OrderUOM>SH</OrderUOM>
<UnitPrice currency='USD'>125.50</UnitPrice>
<NameFromBC/>
<Phone1FromBC/>
<EmailFromBC>katharine.belcher</EmailFromBC>
<DeliverByDate/>
<RequestedShipDate>2021-01-31</RequestedShipDate>
<LineMessage>PRINT 1 COPY(S) OF ALL PDF FIL</LineMessage>
<LineMessage>ES STARTING WITH: 59240249 WVU</LineMessage>
<SupplierAuxPartId/>
<LineItemExtrinsic>
<Extrinsic name='PriceUOM'>M</Extrinsic>
</LineItemExtrinsic>
</LineItem>
</LineItems>
</Order>
</wPOREQ>
Here is what I produced for the XSLT:
<?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" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>
<!--<xsl:value-of select="Header/MessageId"/>-->
</MessageId>
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action>
</Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
<xsl:apply-templates select="wPOREQ/Order"/>
<xsl:apply-templates select="wPOREQ/Order/ShipTo"/>
<xsl:apply-templates select="wPOREQ/Order/LineItems/LineItem"/>
</SalesTable>
</SalesOrder>
</MessageParts>
</Body>
</Envelope>
</xsl:template>
<xsl:template match="wPOREQ/Order">
<CustAccount>
<xsl:value-of select="WCSSCustomerId"/>
</CustAccount>
<CustomerRef>
<xsl:value-of select="ReferenceNumber"/>
</CustomerRef>
<InventLocationId>
<xsl:value-of select="WCSSCustomerId"/>
</InventLocationId>
<InventSiteId>
<xsl:value-of select="WCSSCustomerId"/>
</InventSiteId>
<InvoiceAccount>
<xsl:value-of select="WCSSCustomerId"/>
</InvoiceAccount>
<ProductType>
<xsl:value-of select="WCSSCustomerId"/>
</ProductType>
<PurchOrderFormNum>
<xsl:value-of select="PONumber"/>
</PurchOrderFormNum>
<ReceiptDateRequested>
<xsl:value-of select="DeliverByDate"/>
</ReceiptDateRequested>
<SalesGroup>
<xsl:value-of select="WCSSCustomerId"/>
</SalesGroup>
<SalesPoolId>
<xsl:value-of select="10" />
</SalesPoolId>
<ShipCarrierDeliveryContact>
<xsl:value-of select="ShipTo/Attention"/>
</ShipCarrierDeliveryContact>
</xsl:template>
<xsl:template match="ShipTo">
<TableDlvAddr class="entity">
<City>
<xsl:value-of select="City"/>
</City>
<CountryRegionId>
<xsl:choose>
<xsl:when test="CountryCode = 'US'">USA</xsl:when>
<xsl:otherwise>
<xsl:value-of select="USA" />
</xsl:otherwise>
</xsl:choose>
</CountryRegionId>
<LocationName>
<xsl:value-of select="Name1"/>
</LocationName>
<State>
<xsl:value-of select="State"/>
</State>
<Street>
<xsl:value-of select="concat(Address1,'
', Address2,'
', Address3)"/>
</Street>
<ZipCode>
<xsl:value-of select="substring(ZipCode,1,5)"/>
</ZipCode>
</TableDlvAddr>
</xsl:template>
<xsl:template match="LineItem">
<SalesLine class="entity">
<CustomerRef>
<xsl:value-of select="../../ReferenceNumber"/>
</CustomerRef>
<ItemId>
<xsl:value-of select="SKU"/>
</ItemId>
<LineNum>
<xsl:value-of select="LineNumber"/>
</LineNum>
<PurchorderFormNum>
<xsl:value-of select="../../PONumber"/>
</PurchorderFormNum>
<SalesPrice>
<xsl:value-of select="UnitPrice"/>
</SalesPrice>
<SalesQty>
<xsl:value-of select="Quantity"/>
</SalesQty>
<SalesUnit>
<xsl:value-of select="OrderUOM"/>
</SalesUnit>
</SalesLine>
</xsl:template>
</xsl:stylesheet>
However, the output produces:
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId />
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action>
</Header>
<Body>
<MessageParts>
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
<CustAccount xmlns="">0810147</CustAccount>
<CustomerRef xmlns="">BK012521-01</CustomerRef>
<InventLocationId xmlns="">0810147</InventLocationId>
<InventSiteId xmlns="">0810147</InventSiteId>
<InvoiceAccount xmlns="">0810147</InvoiceAccount>
<ProductType xmlns="">0810147</ProductType>
<PurchOrderFormNum xmlns="">595280620001b3</PurchOrderFormNum>
<ReceiptDateRequested xmlns="">2021-01-31</ReceiptDateRequested>
<SalesGroup xmlns="">0810147</SalesGroup>
<SalesPoolId xmlns="">10</SalesPoolId>
<ShipCarrierDeliveryContact xmlns="">RM:8707,SALLY WEAVER</ShipCarrierDeliveryContact>
<TableDlvAddr class="entity" xmlns="">
<City>MORGANTOWN</City>
<CountryRegionId>USA</CountryRegionId>
<LocationName>WEST VIRGINIA UNIVERSITY</LocationName>
<State>WV</State>
<Street>
64 MEDICAL CENTER DRIVE
HSC BMRC
BK TEST
</Street>
<ZipCode>26505</ZipCode>
</TableDlvAddr>
<SalesLine class="entity" xmlns="">
<CustomerRef>BK012521-01</CustomerRef>
<ItemId>10037475</ItemId>
<LineNum>1</LineNum>
<PurchorderFormNum>595280620001b3</PurchorderFormNum>
<SalesPrice>125.50</SalesPrice>
<SalesQty>1000</SalesQty>
<SalesUnit>SH</SalesUnit>
</SalesLine>
</SalesTable>
</SalesOrder>
</MessageParts>
</Body>
</Envelope>
Desired output is this:
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId />
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action>
</Header>
<Body>
<MessageParts>
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
<CustAccount>0810147</CustAccount>
<CustomerRef>59240249</CustomerRef>
<InventLocationId>0810147</InventLocationId>
<InventSiteId>0810147</InventSiteId>
<InvoiceAccount>0810147</InvoiceAccount>
<ProductType>0810147</ProductType>
<PurchOrderFormNum>595280620001</PurchOrderFormNum>
<ReceiptDateRequested>2021-01-31</ReceiptDateRequested>
<SalesGroup>0810147</SalesGroup>
<SalesPoolId>10</SalesPoolId>
<ShipCarrierDeliveryContact>RM:8707</ShipCarrierDeliveryContact>
<TableDlvAddr class="entity">
<City>MORGANTOWN</City>
<CountryRegionId>USA </CountryRegionId>
<LocationName>WEST VIRGINIA UNIVERSITY</LocationName>
<State>WV</State>
<Street>64 MEDICAL CENTER DRIVE
HSC BMRC
BK TEST</Street>
<ZipCode>26505</ZipCode>
</TableDlvAddr>
<SalesLine class="entity">
<CustomerRef>59240249</CustomerRef>
<ItemId>10037475</ItemId>
<LineNum>1</LineNum>
<PurchOrderFormNum>595280620001</PurchOrderFormNum>
<SalesPrice>125.50</SalesPrice>
<SalesQty>1000</SalesQty>
<SalesUnit>SH</SalesUnit>
</SalesLine>
</SalesTable>
</SalesOrder>
</MessageParts>
</Body>
</Envelope>
Declare those namespaces with namespace-prefixes in your stylesheet, and use those namespace-prefixes on the elements that you do want bound to a namespace.
Then the elements which are not bound to a namespace will be serialized without a prefix and won't have to re-declare that the namespace is empty:
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:so="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<m:Envelope>
<m:Header>
<m:MessageId>
<!--<xsl:value-of select="Header/MessageId"/>-->
</m:MessageId>
<m:Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</m:Action>
</m:Header>
<m:Body>
<m:MessageParts>
<so:SalesOrder>
<so:SalesTable class="entity">
<xsl:apply-templates select="wPOREQ/Order"/>
<xsl:apply-templates select="wPOREQ/Order/ShipTo"/>
<xsl:apply-templates select="wPOREQ/Order/LineItems/LineItem"/>
</so:SalesTable>
</so:SalesOrder>
</m:MessageParts>
</m:Body>
</m:Envelope>
</xsl:template>
<xsl:template match="wPOREQ/Order">
<CustAccount>
<xsl:value-of select="WCSSCustomerId"/>
</CustAccount>
<CustomerRef>
<xsl:value-of select="ReferenceNumber"/>
</CustomerRef>
<InventLocationId>
<xsl:value-of select="WCSSCustomerId"/>
</InventLocationId>
<InventSiteId>
<xsl:value-of select="WCSSCustomerId"/>
</InventSiteId>
<InvoiceAccount>
<xsl:value-of select="WCSSCustomerId"/>
</InvoiceAccount>
<ProductType>
<xsl:value-of select="WCSSCustomerId"/>
</ProductType>
<PurchOrderFormNum>
<xsl:value-of select="PONumber"/>
</PurchOrderFormNum>
<ReceiptDateRequested>
<xsl:value-of select="DeliverByDate"/>
</ReceiptDateRequested>
<SalesGroup>
<xsl:value-of select="WCSSCustomerId"/>
</SalesGroup>
<SalesPoolId>
<xsl:value-of select="10" />
</SalesPoolId>
<ShipCarrierDeliveryContact>
<xsl:value-of select="ShipTo/Attention"/>
</ShipCarrierDeliveryContact>
</xsl:template>
<xsl:template match="ShipTo">
<TableDlvAddr class="entity">
<City>
<xsl:value-of select="City"/>
</City>
<CountryRegionId>
<xsl:choose>
<xsl:when test="CountryCode = 'US'">USA</xsl:when>
<xsl:otherwise>
<xsl:value-of select="USA" />
</xsl:otherwise>
</xsl:choose>
</CountryRegionId>
<LocationName>
<xsl:value-of select="Name1"/>
</LocationName>
<State>
<xsl:value-of select="State"/>
</State>
<Street>
<xsl:value-of select="concat(Address1,'
', Address2,'
', Address3)"/>
</Street>
<ZipCode>
<xsl:value-of select="substring(ZipCode,1,5)"/>
</ZipCode>
</TableDlvAddr>
</xsl:template>
<xsl:template match="LineItem">
<SalesLine class="entity">
<CustomerRef>
<xsl:value-of select="../../ReferenceNumber"/>
</CustomerRef>
<ItemId>
<xsl:value-of select="SKU"/>
</ItemId>
<LineNum>
<xsl:value-of select="LineNumber"/>
</LineNum>
<PurchorderFormNum>
<xsl:value-of select="../../PONumber"/>
</PurchorderFormNum>
<SalesPrice>
<xsl:value-of select="UnitPrice"/>
</SalesPrice>
<SalesQty>
<xsl:value-of select="Quantity"/>
</SalesQty>
<SalesUnit>
<xsl:value-of select="OrderUOM"/>
</SalesUnit>
</SalesLine>
</xsl:template>
</xsl:stylesheet>
Produces this output:
<?xml version="1.0" encoding="utf-8"?>
<m:Envelope xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:so="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<m:Header>
<m:MessageId/>
<m:Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</m:Action>
</m:Header>
<m:Body>
<m:MessageParts>
<so:SalesOrder>
<so:SalesTable class="entity">
<CustAccount>0810999</CustAccount>
<CustomerRef>59240249</CustomerRef>
<InventLocationId>0810999</InventLocationId>
<InventSiteId>0810999</InventSiteId>
<InvoiceAccount>0810999</InvoiceAccount>
<ProductType>0810999</ProductType>
<PurchOrderFormNum>59test</PurchOrderFormNum>
<ReceiptDateRequested>2021-01-31</ReceiptDateRequested>
<SalesGroup>0810999</SalesGroup>
<SalesPoolId>10</SalesPoolId>
<ShipCarrierDeliveryContact>RM:8707</ShipCarrierDeliveryContact>
<TableDlvAddr class="entity">
<City>ANYTOWN</City>
<CountryRegionId>USA</CountryRegionId>
<LocationName>SHIP TO THIS</LocationName>
<State>WV</State>
<Street>99 MEDICAL CENTER DRIVE
HSC BMRC
BK TEST</Street>
<ZipCode>26505</ZipCode>
</TableDlvAddr>
<SalesLine class="entity">
<CustomerRef>59240249</CustomerRef>
<ItemId>10037475</ItemId>
<LineNum>1</LineNum>
<PurchorderFormNum>59test</PurchorderFormNum>
<SalesPrice>125.50</SalesPrice>
<SalesQty>1000</SalesQty>
<SalesUnit>SH</SalesUnit>
</SalesLine>
</so:SalesTable>
</so:SalesOrder>
</m:MessageParts>
</m:Body>
</m:Envelope>
If you wanted those SalesOrder descendant elements which were being serialized in the "no namespace" to be bound to the sales order namespace, but still not have namespace prefixes, then declare the default namespace (no namespace-prefix) to be the sales order namespace and retain the explicit declaration of namespaces for the Envelope and SalesOrder elements:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>
<!--<xsl:value-of select="Header/MessageId"/>-->
</MessageId>
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action>
</Header>
<Body>
<MessageParts>
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
<xsl:apply-templates select="wPOREQ/Order"/>
<xsl:apply-templates select="wPOREQ/Order/ShipTo"/>
<xsl:apply-templates select="wPOREQ/Order/LineItems/LineItem"/>
</SalesTable>
</SalesOrder>
</MessageParts>
</Body>
</Envelope>
</xsl:template>
<xsl:template match="wPOREQ/Order">
<CustAccount>
<xsl:value-of select="WCSSCustomerId"/>
</CustAccount>
<CustomerRef>
<xsl:value-of select="ReferenceNumber"/>
</CustomerRef>
<InventLocationId>
<xsl:value-of select="WCSSCustomerId"/>
</InventLocationId>
<InventSiteId>
<xsl:value-of select="WCSSCustomerId"/>
</InventSiteId>
<InvoiceAccount>
<xsl:value-of select="WCSSCustomerId"/>
</InvoiceAccount>
<ProductType>
<xsl:value-of select="WCSSCustomerId"/>
</ProductType>
<PurchOrderFormNum>
<xsl:value-of select="PONumber"/>
</PurchOrderFormNum>
<ReceiptDateRequested>
<xsl:value-of select="DeliverByDate"/>
</ReceiptDateRequested>
<SalesGroup>
<xsl:value-of select="WCSSCustomerId"/>
</SalesGroup>
<SalesPoolId>
<xsl:value-of select="10" />
</SalesPoolId>
<ShipCarrierDeliveryContact>
<xsl:value-of select="ShipTo/Attention"/>
</ShipCarrierDeliveryContact>
</xsl:template>
<xsl:template match="ShipTo">
<TableDlvAddr class="entity">
<City>
<xsl:value-of select="City"/>
</City>
<CountryRegionId>
<xsl:choose>
<xsl:when test="CountryCode = 'US'">USA</xsl:when>
<xsl:otherwise>
<xsl:value-of select="USA" />
</xsl:otherwise>
</xsl:choose>
</CountryRegionId>
<LocationName>
<xsl:value-of select="Name1"/>
</LocationName>
<State>
<xsl:value-of select="State"/>
</State>
<Street>
<xsl:value-of select="concat(Address1,'
', Address2,'
', Address3)"/>
</Street>
<ZipCode>
<xsl:value-of select="substring(ZipCode,1,5)"/>
</ZipCode>
</TableDlvAddr>
</xsl:template>
<xsl:template match="LineItem">
<SalesLine class="entity">
<CustomerRef>
<xsl:value-of select="../../ReferenceNumber"/>
</CustomerRef>
<ItemId>
<xsl:value-of select="SKU"/>
</ItemId>
<LineNum>
<xsl:value-of select="LineNumber"/>
</LineNum>
<PurchorderFormNum>
<xsl:value-of select="../../PONumber"/>
</PurchorderFormNum>
<SalesPrice>
<xsl:value-of select="UnitPrice"/>
</SalesPrice>
<SalesQty>
<xsl:value-of select="Quantity"/>
</SalesQty>
<SalesUnit>
<xsl:value-of select="OrderUOM"/>
</SalesUnit>
</SalesLine>
</xsl:template>
</xsl:stylesheet>
Which produces this output:
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId/>
<Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action>
</Header>
<Body>
<MessageParts>
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
<CustAccount>0810999</CustAccount>
<CustomerRef>59240249</CustomerRef>
<InventLocationId>0810999</InventLocationId>
<InventSiteId>0810999</InventSiteId>
<InvoiceAccount>0810999</InvoiceAccount>
<ProductType>0810999</ProductType>
<PurchOrderFormNum>59test</PurchOrderFormNum>
<ReceiptDateRequested>2021-01-31</ReceiptDateRequested>
<SalesGroup>0810999</SalesGroup>
<SalesPoolId>10</SalesPoolId>
<ShipCarrierDeliveryContact>RM:8707</ShipCarrierDeliveryContact>
<TableDlvAddr class="entity">
<City>ANYTOWN</City>
<CountryRegionId>USA</CountryRegionId>
<LocationName>SHIP TO THIS</LocationName>
<State>WV</State>
<Street>99 MEDICAL CENTER DRIVE
HSC BMRC
BK TEST</Street>
<ZipCode>26505</ZipCode>
</TableDlvAddr>
<SalesLine class="entity">
<CustomerRef>59240249</CustomerRef>
<ItemId>10037475</ItemId>
<LineNum>1</LineNum>
<PurchorderFormNum>59test</PurchorderFormNum>
<SalesPrice>125.50</SalesPrice>
<SalesQty>1000</SalesQty>
<SalesUnit>SH</SalesUnit>
</SalesLine>
</SalesTable>
</SalesOrder>
</MessageParts>
</Body>
</Envelope>

XSLT for flat to nested/hierarchical, with level interpolation?

Given this source XML document: input.xml
<body>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="3">content</p>
</body>
I'd like to transform to output.xml:
<list>
<item>
<list>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
etc
Attribute ilvl is the list level; its a zero-based index.
I tried adapting https://stackoverflow.com/a/11117548/1031689 and got output:
<rs>
<p ilvl="1"/>
<p ilvl="1">
<p ilvl="2">
<p ilvl="3"/>
</p>
</p>
<p ilvl="1">
<p ilvl="2"/>
<p ilvl="2">
<p ilvl="3"/>
</p>
</p>
<p ilvl="1"/>
<p ilvl="1">
<p ilvl="3"/>
</p>
</rs>
I have 2 issues with it:
It doesn't create structure for any missing level (eg missing level 2 between 1 and 3), and
The starting param level must match the first entry (ie 1 here). If you pass 0, the nesting is wrong.
Before I tried this, I was using my own XSLT 1.0 code, attached below.
The tricky part is how to handle a decrease in nesting eg level 3 to 1:
<p ilvl="3">content</p>
<p ilvl="1">content</p>
Updated
I try to handle this in the addList template, as the recursion is "unwound", but its not quite right yet; in my output when it gets back to level 1 a new list is being inserted, but if I correct that, I drop the last 3 content items... If anyone can solve this, I'll be impressed :-)
Yeah, I know my code is way more complicated, so if there is any easy fix to the for-each-group approach above, it'd be great to have suggestions.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!-- works, except makes new list for siblings -->
<xsl:template name="listSection">
<xsl:param name="last-level">-1</xsl:param>
<xsl:param name="items"/>
<xsl:variable name="currentItem" select="$items[1]"/>
<xsl:variable name="currentLevel">
<xsl:value-of select="number($currentItem/#ilvl)"/>
</xsl:variable>
<xsl:variable name="nextItems" select="$items[position() > 1]"/>
<xsl:choose>
<xsl:when test="$currentLevel = $last-level">
<!-- just add an item -->
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$currentItem"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
<!-- that handles next level higher case, and level same case-->
<!-- level lower is handled is addList template-->
</xsl:when>
<xsl:when test="$currentLevel > $last-level">
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$last-level"/>
<xsl:with-param name="nextItems" select="$items"/> <!-- since haven't handled current item yet -->
</xsl:call-template>
</xsl:when>
<xsl:otherwise> this level < last level: should not happen?</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="addItem">
<xsl:param name="currentItem"/>
<xsl:param name="nextItems"/>
<xsl:variable name="currentLevel">
<xsl:value-of select="number($currentItem/#ilvl)"/>
</xsl:variable>
<item>
<xsl:apply-templates select="$currentItem"/>
<!-- is the next level higher?-->
<xsl:if test="(count($nextItems) > 0) and
(number($nextItems[1]/#ilvl) > $currentLevel)">
<!-- insert list/item to the necessary depth-->
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$currentLevel"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
</xsl:if>
</item>
<!-- next level same-->
<xsl:if test="(count($nextItems) > 0) and
(number($nextItems[1]/#ilvl) = $currentLevel)">
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$nextItems[1]"/>
<xsl:with-param name="nextItems" select="$nextItems[position() > 1]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="addList">
<xsl:param name="currentLevel">-1</xsl:param>
<xsl:param name="nextItems"/>
<xsl:variable name="targetLevel">
<xsl:value-of select="number($nextItems[1]/#ilvl)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$targetLevel - $currentLevel > 1">
<!-- interpolate -->
<list>
<xsl:variable name="stuff">
<item>
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$currentLevel+1"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
</item>
</xsl:variable>
<xsl:copy-of select="$stuff"/>
<xsl:variable name="currentPos" select="count(msxsl:node-set($stuff)//p)" />
<xsl:variable name="ascentLevel">
<xsl:value-of select="number($nextItems[$currentPos]/#ilvl)"/>
</xsl:variable>
<xsl:variable name="ascentItems" select="$nextItems[position() > $currentPos]"/>
<xsl:variable name="aftertargetLevel">
<xsl:value-of select="number($ascentItems[1]/#ilvl)"/>
</xsl:variable>
<xsl:if test="(count($ascentItems) > 1) and
($aftertargetLevel - $currentLevel = 1)">
<xsl:call-template name="listSection">
<xsl:with-param name="last-level" select="$currentLevel"/>
<xsl:with-param name="items" select="$ascentItems"/>
</xsl:call-template>
</xsl:if>
</list>
</xsl:when>
<xsl:when test="$targetLevel - $currentLevel = 1">
<!-- insert real item -->
<xsl:variable name="stuff">
<list>
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$nextItems[1]"/>
<xsl:with-param name="nextItems" select="$nextItems[position() > 1]"/>
</xsl:call-template>
</list>
</xsl:variable>
<!-- might be items on the way out -->
<xsl:copy-of select="$stuff"/>
<xsl:variable name="currentPos" select="count(msxsl:node-set($stuff)//p)" />
<xsl:variable name="ascentLevel">
<xsl:value-of select="number($nextItems[$currentPos]/#ilvl)"/>
</xsl:variable>
<xsl:variable name="ascentItems" select="$nextItems[position() > $currentPos]"/>
<xsl:variable name="aftertargetLevel">
<xsl:value-of select="number($ascentItems[1]/#ilvl)"/>
</xsl:variable>
<xsl:if test="(count($ascentItems) > 1) and
($aftertargetLevel - $currentLevel = 1)">
<xsl:call-template name="listSection">
<xsl:with-param name="last-level" select="$currentLevel"/>
<xsl:with-param name="items" select="$ascentItems"/>
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<!--should not happen!-->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:call-template name="listSection">
<xsl:with-param name="items" select="*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
This is my second attempt to provide solution to the problem which, in its current state forces people (at least me) to guess what is wanted:
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pStartLevel" select="1"/>
<xsl:key name="kChildren" match="p"
use="generate-id(preceding-sibling::p
[not(#ilvl >= current()/#ilvl)][1])"/>
<xsl:template match="/*">
<list>
<item>
<xsl:apply-templates select="key('kChildren', '')[1]" mode="start">
<xsl:with-param name="pParentLevel" select="$pStartLevel"/>
<xsl:with-param name="pSiblings" select="key('kChildren', '')"/>
</xsl:apply-templates>
</item>
</list>
</xsl:template>
<xsl:template match="p" mode="start">
<xsl:param name="pParentLevel"/>
<xsl:param name="pSiblings"/>
<list>
<xsl:apply-templates select="$pSiblings">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
</list>
</xsl:template>
<xsl:template match="p">
<xsl:param name="pParentLevel"/>
<xsl:apply-templates select="self::*[#ilvl - $pParentLevel > 1]"
mode="buildMissingLevels">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
<xsl:apply-templates select="self::*[not(#ilvl - $pParentLevel > 1)]" mode="normal">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="p" mode="normal">
<xsl:param name="pParentLevel"/>
<item>
<xsl:copy-of select="."/>
<xsl:apply-templates mode="start"
select="key('kChildren',generate-id())[1]">
<xsl:with-param name="pParentLevel" select="#ilvl"/>
<xsl:with-param name="pSiblings"
select="key('kChildren',generate-id())"/>
</xsl:apply-templates>
</item>
</xsl:template>
<xsl:template match="p" mode="buildMissingLevels">
<xsl:param name="pParentLevel"/>
<item>
<p ilvl="{$pParentLevel +1}"/>
<list>
<xsl:apply-templates select=".">
<xsl:with-param name="pParentLevel" select="$pParentLevel +1"/>
</xsl:apply-templates>
</list>
</item>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML document:
<body>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="3">content</p>
</body>
produces what I believe is wanted:
<list>
<item>
<list>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
</item>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2"/>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
</list>
</item>
</list>

Grouping the consecutive elements in XSLT

I am new to XSLT and XML. I have the following XML. I wanted to group the consecutive child elements.
<Root>
<Child No="1" Month="0" Date="13/08/2014" Payment="100">
<Totals/>
</Child>
<Child No="2" Month="1" Date="13/09/2014" Payment="100">
<Totals/>
</Child>
<Child No="3" Month="2" Date="13/10/2014" Payment="200">
<Totals/>
</Child>
<Child No="4" Month="3" Date="13/11/2014" Payment="300">
<Totals/>
</Child>
<Child No="5" Month="4" Date="13/12/2014" Payment="300">
<Totals/>
</Child>
<Child No="6" Month="5" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="7" Month="6" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="8" Month="7" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="9" Month="8" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="10" Month="9" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
</Root>
I need to create a new PP node when Child[n]/Payment <> Child[n-1]/Payment using the XSLT.
And I am expecting the below result,
<PPS>
<PP>
<Ps>
<StartMonth>0</StartMonth>
<EndMonth>1</EndMonth>
<P>
<Amount>100</Amount>
<startP>1</startP>
</P>
<P>
<Amount>100</Amount>
<startP>2</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>2</StartMonth>
<EndMonth>2</EndMonth>
<P>
<Amount>200</Amount>
<startP>3</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>3</StartMonth>
<EndMonth>4</EndMonth>
<P>
<Amount>300</Amount>
<startP>4</startP>
</P>
<P>
<Amount>300</Amount>
<startP>5</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>5</StartMonth>
<EndMonth>9</EndMonth>
<P>
<Amount>100</Amount>
<startP>6</startP>
</P>
<P>
<Amount>100</Amount>
<startP>7</startP>
</P>
<P>
<Amount>100</Amount>
<startP>8</startP>
</P>
<P>
<Amount>100</Amount>
<startP>9</startP>
</P>
<P>
<Amount>100</Amount>
<startP>10</startP>
</P>
</Ps>
</PP>
</PPS>
Here is my sample xslt
<xsl:stylesheet version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="x" match="Child" use="#Payment"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="CF">
<xsl:if test="generate-id(.) = generate-id(key('x',#Payment)[1])">
<PP>
<Ps>
<xsl:for-each select="key('x', #Payment)">
<P>
<Amount><xsl:value-of select="format-number(translate(#Payment, ',','.'),'0.00')"/></Amount>
<startP><xsl:value-of select="#Date"/></startP>
</P>
</xsl:for-each>
</Ps>
</PP>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
</xsl:stylesheet>
This would work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="Child">
<xsl:variable name="Payment" select="#Payment"/>
<xsl:if test="not(preceding::*[1][self::Child and #Payment = $Payment])">
<PPS>
<Ps>
<xsl:apply-templates select="current()"/>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</Ps>
</PPS>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
<xsl:template match="Child">
<xsl:param name="Payment"/>
<P>
<Amount>
<xsl:value-of select="#Payment"/>
</Amount>
<startP>
<xsl:value-of select="#No"/>
</startP>
</P>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]"/>
</xsl:template>
</xsl:stylesheet>

Ordering nodes in XSL Transformation

Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData version="2.1" xmlns="bank.xsd">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BANK" distName="CITY-1/ABC-1/BANK-1" operation="create" timeStamp="2013-07-27T15:48:20"/>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="update" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/BANK-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2" operation="update" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Transformation XSL :
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:x="bank.xsd" exclude-result-prefixes="x">
<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="x:object[#class = 'BANK' ]">
</xsl:template>
<xsl:template match="x:object[#class = 'Branch' ]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItem' ]">
<xsl:variable name="branchItem" select="."/>
<xsl:choose>
<xsl:when test="$branchItem/#operation='update' and not(contains($branchItem/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItemPeriod' ]">
<xsl:variable name="branchPeiod" select="."/>
<xsl:choose>
<xsl:when test="$branchPeiod/#operation='update' and not(contains($branchPeiod/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output XML :
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Desired OUTPUT XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
I could achieve most of desired output except for few...
I want the output to be sorted based on distName attribute of object nodes.
I want the sort to happen only to certain child nodes with class names as Branch , BranchItem , BranchItemPeriod.
Here i try for update with delete and create operation, so i want also to maintain the order of delete and create too which i do in present transformation logic or else can it so happen that i sort all first based on above criteria and apply the other transformation logic.
Any suggestion or help is highly appreciated.
I think what you need here is a template that matches the InsideData element, where you can then select the child object elements in the order you require.
You would first start by outputting the non-"object" elements, assuming these always come before the object elements.
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
Then you would select the object elements with the class attribute you desire, sorting in the order you require too:
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
Finally, you would output the object elements which have the the class attributes.
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
Try adding this template to your XSLT to see how you get on:
<xsl:template match="x:InsideData">
<xsl:copy>
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
</xsl:copy>
</xsl:template>

XSL check param length and make a choice

I need to check if a param has got a value in it and if it has then do this line otherwise do this line.
I've got it working whereas I don't get errors but it's not taking the right branch
The branch that is wrong is in the volunteer_role template
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="volunteers-by-region" match="volunteer" use="region" />
<xsl:template name="hoo" match="/">
<html>
<head>
<title>Registered Volunteers</title>
<link rel="stylesheet" type="text/css" href="volunteer.css" />
</head>
<body>
<h1>Registered Volunteers</h1>
<h3>Ordered by the username ascending</h3>
<xsl:for-each select="folktask/member[user/account/userlevel='2']">
<xsl:for-each select="volunteer[count(. | key('volunteers-by-region', region)[1]) = 1]">
<xsl:sort select="region" />
<xsl:for-each select="key('volunteers-by-region', region)">
<xsl:sort select="folktask/member/user/personal/name" />
<div class="userdiv">
<xsl:call-template name="volunteer_volid">
<xsl:with-param name="volid" select="../volunteer/#id" />
</xsl:call-template>
<xsl:call-template name="volunteer_role">
<xsl:with-param name="volrole" select="../volunteer/roles" />
</xsl:call-template>
<xsl:call-template name="volunteer_region">
<xsl:with-param name="volloc" select="../volunteer/region" />
</xsl:call-template>
</div>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:if test="position()=last()">
<div class="count">
<h2>Total number of volunteers: <xsl:value-of select="count(/folktask/member/user/account/userlevel[text()=2])" />
</h2>
</div>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template name="volunteer_volid">
<xsl:param name="volid" select="'Not Available'" />
<div class="heading2 bold"><h2>VOLUNTEER ID: <xsl:value-of select="$volid" /></h2></div>
</xsl:template>
<xsl:template name="volunteer_role">
<xsl:param name="volrole" select="'Not Available'" />
<div class="small bold">ROLES:</div>
<div class="large">
<xsl:choose>
<xsl:when test="string-length($volrole)!=0">
<xsl:value-of select="$volrole" />
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:template>
<xsl:template name="volunteer_region">
<xsl:param name="volloc" select="'Not Available'" />
<div class="small bold">REGION:</div>
<div class="large"><xsl:value-of select="$volloc" /></div>
</xsl:template>
</xsl:stylesheet>
And the XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="volunteers.xsl"?>
<folktask xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="folktask.xsd">
<member>
<user id="1">
<personal>
<name>Abbie Hunt</name>
<sex>Female</sex>
<address1>108 Access Road</address1>
<address2></address2>
<city>Wells</city>
<county>Somerset</county>
<postcode>BA5 8GH</postcode>
<telephone>01528927616</telephone>
<mobile>07085252492</mobile>
<email>adrock#gmail.com</email>
</personal>
<account>
<username>AdRock</username>
<password>269eb625e2f0cf6fae9a29434c12a89f</password>
<userlevel>4</userlevel>
<signupdate>2010-03-26T09:23:50</signupdate>
</account>
</user>
<volunteer id="1">
<roles></roles>
<region>South West</region>
</volunteer>
</member>
<member>
<user id="2">
<personal>
<name>Aidan Harris</name>
<sex>Male</sex>
<address1>103 Aiken Street</address1>
<address2></address2>
<city>Chichester</city>
<county>Sussex</county>
<postcode>PO19 4DS</postcode>
<telephone>01905149894</telephone>
<mobile>07784467941</mobile>
<email>ambientexpert#yahoo.co.uk</email>
</personal>
<account>
<username>AmbientExpert</username>
<password>8e64214160e9dd14ae2a6d9f700004a6</password>
<userlevel>2</userlevel>
<signupdate>2010-03-26T09:23:50</signupdate>
</account>
</user>
<volunteer id="2">
<roles>Van Driver,gas Fitter</roles>
<region>South Central</region>
</volunteer>
</member>
</folktask>
The following should do the trick:
<xsl:template name="volunteer_volid">
<xsl:param name="volid" />
<xsl:choose>
<xsl:when test="string-length($volid) > 0">
<div class="heading2 bold"><h2>VOLUNTEER ID: <xsl:value-of select="$volid" /></h2></div>
</xsl:when>
<xsl:otherwise>
<!-- No volid -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I've replaced the default value with an empty string so that not providing a parameter value is the same as providing the parameter value as "". If this isn't the desired behaviour then change the parameters select and modify the test expression accordingly, for example:
$volid != 'Not Available'