XSLT transform with templates: remove empty xmlns="" - xslt

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>

Related

XSLT -One output in SSIS

I have a XML file when I use SSIS XML as Source it is generating multiple outputs. I am using XML task to design XSLT for getting single output.
Following is the XML. I need only single output in SSIS. How to write XSLT for the same?
<?xml version="1.0" encoding="utf-8"?>
<Test>
<iden type="shiftid">100</iden >
<iden type="trailid">25</iden >
<TestHeader>
<SequenceNum>111</SequenceNum>
<TestType>CONTAINER</TestType>
<hub role="origin">
<name />
<hubId>030</hubId>
<hubType>New</hubType>
</hub>
<hub role="Dest">
<name />
<hubId>0757</hubId>
<hubType>hublet</hubType>
</hub>
<TestItem>
<lineNumber>1</lineNumber>
<container>1005</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>TRANS-SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
<TestItem>
<lineNumber>2</lineNumber>
<container>1005</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
<TestItem>
<lineNumber>1</lineNumber>
<container>1006</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
</TestHeader>
<TestContainer type="RAPP" id="1005">
<uom hb="m3" type="board">10.0729</uom>
</TestContainer>
<TestContainer type="RAMP" id="1006">
<uom hb="m3" type="board">10.0729</uom>
</TestContainer>
</Test>
I have struggled and comeup with XSLT as follows
<?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"/>
<xsl:template name="Iden" match="Test">
<xsl:for-each select="../iden">
<xsl:choose>
<xsl:when test="#type='shiftid'">
<ShipmentID>
<xsl:value-of select="."/>
</ShipmentID>
</xsl:when>
<xsl:when test="#type='trailid'">
<TrailerID>
<xsl:value-of select="."/>
</TrailerID>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template name="Item" match="Test">
<xsl:param name="MatchContainer"/>
<xsl:for-each select="//TestItem">
<xsl:if test="$MatchContainer=container">
<Testitem>
<lineNumber>
<xsl:value-of select="lineNumber"/>
</lineNumber>
<containerTestitemID>
<xsl:value-of select="container"/>
</containerTestitemID>
<ConditionCode>
<xsl:value-of select="conditionCode"/>
</ConditionCode>
<Originator>
<xsl:value-of select="hub[#role='originator']/hubId"/>
</Originator>
<OriginatorType>
<xsl:value-of select="hub[#role='originator']/hubType"/>
</OriginatorType>
</Testitem>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="container" match="Test">
<xsl:for-each select="../TestContainer">
<Testcontainerlist>
<ContainerType>
<xsl:value-of select="#type"/>
</ContainerType>
<ContainerID>
<xsl:value-of select="#id"/>
</ContainerID>
<Volume>
<xsl:value-of select="uom"/>
</Volume>
<VolumeUOM>
<xsl:value-of select="volume/#hb"/>
</VolumeUOM>
<VolumeType>
<xsl:value-of select="volume/#type"/>
</VolumeType>
<xsl:call-template name="Item">
<xsl:with-param name="MatchContainer" select="#id"/>
</xsl:call-template>
</Testcontainerlist>
</xsl:for-each>
</xsl:template>
<xsl:template name="Header" match="Test">
<xsl:for-each select="TestHeader">
<Test>
<xsl:call-template name="Iden">
</xsl:call-template>
<dropSequenceNumber>
<xsl:value-of select="SequenceNum"/>
</dropSequenceNumber>
<TestType>
<xsl:value-of select="TestType"/>
</TestType>
<OriginID>
<xsl:value-of select="hub[#role='origin']/hubId"/>
</OriginID>
<DestinationID>
<xsl:value-of select="hub[#role='Dest']/hubId"/>
</DestinationID>
<OriginType>
<xsl:value-of select="hub[#role='origin']/hubType"/>
</OriginType>
<DestinationType>
<xsl:value-of select="hub[#role='Dest']/hubType"/>
</DestinationType>
<xsl:call-template name="container">
</xsl:call-template>
</Test>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
My desired output should be as follows
<?xml version="1.0" encoding="utf-8"?>
<Test>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAPP</ContainerType>
<ContainerID>1005</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>1</lineNumber>
<containerTestitemID>1005</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>TRANS-SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAPP</ContainerType>
<ContainerID>1005</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>2</lineNumber>
<containerTestitemID>1005</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAMP</ContainerType>
<ContainerID>1006</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>1</lineNumber>
<containerTestitemID>1006</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
</Test>

Select repeating node with xpath

I am using XSLT 1.0
I have the following xml document
<ns0:Root xmlns:ns0="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputMessagePart_0>
<ns1:ArrayOfArticleMasterDTO xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster">
<ArticleMasterDTO>
<AltBarcodes>
<string>5020436473709</string>
</AltBarcodes>
<ClientId>GO01</ClientId>
<ArticleID>100000005503</ArticleID>
<ArticleReference>CORE_OWBA_WW873R</ArticleReference>
<ArticleSize>14</ArticleSize>
<ArticleStyle>26600</ArticleStyle>
<ArticleFit>-</ArticleFit>
<SupplierID>50102</SupplierID>
<Description>AMELIE OTRS [REG]</Description>
<Sku>COREOWBAWW873RBLAC-14</Sku>
<UnitsPerCase>1</UnitsPerCase>
<UnitsPerLayer xsi:nil="true" />
<UnitsPerPallet xsi:nil="true" />
<LayersPerPallet xsi:nil="true" />
<FullPalletWeightKG xsi:nil="true" />
<CaseLengthMM xsi:nil="true" />
<CaseHeightMM xsi:nil="true" />
<CaseDepthMM xsi:nil="true" />
<CaseWeightG xsi:nil="true" />
<Colour>BLAC</Colour>
<Level1Description>CLOTHING</Level1Description>
<Level2Description>WOMENS</Level2Description>
<Level3Description>WP LEGWEAR</Level3Description>
<Level4Description>LEG COATED</Level4Description>
<PrimaryBarcode>5051513724896</PrimaryBarcode>
</ArticleMasterDTO>
<ArticleMasterDTO>
<AltBarcodes>
<string>5027793433728</string>
</AltBarcodes>
<ClientId>GO01</ClientId>
<ArticleID>100000032177</ArticleID>
<ArticleReference>CORE_OMOE_47354</ArticleReference>
<ArticleSize>L-XL</ArticleSize>
<ArticleStyle>24608</ArticleStyle>
<ArticleFit>-</ArticleFit>
<SupplierID>50013</SupplierID>
<Description>POWER STRETCH GLOVE</Description>
<Sku>COREOMOE47354BLAC-L-XL</Sku>
<UnitsPerCase>6</UnitsPerCase>
<UnitsPerLayer xsi:nil="true" />
<UnitsPerPallet xsi:nil="true" />
<LayersPerPallet xsi:nil="true" />
<FullPalletWeightKG xsi:nil="true" />
<CaseLengthMM xsi:nil="true" />
<CaseHeightMM xsi:nil="true" />
<CaseDepthMM xsi:nil="true" />
<CaseWeightG xsi:nil="true" />
<Colour>BLAC</Colour>
<Level1Description>CLOTHING</Level1Description>
<Level2Description>MENS</Level2Description>
<Level3Description>GLOVES</Level3Description>
<Level4Description>FLEECE GLOVE</Level4Description>
<PrimaryBarcode>5052071278609</PrimaryBarcode>
</ArticleMasterDTO>
</ns1:ArrayOfArticleMasterDTO>
</InputMessagePart_0>
<InputMessagePart_1>
<ns2:SelectResponse xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES">
<ns2:SelectResult>
<ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:PRODUCT_ID>100000005503</ns2:PRODUCT_ID>
<ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
<ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
<ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode5503</ns2:CHECK_DIGIT_PACK_BARCODE>
<ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
<ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
<ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
<ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
<ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
<ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
</ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:PRODUCT_ID>100000032177</ns2:PRODUCT_ID>
<ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
<ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
<ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode32177</ns2:CHECK_DIGIT_PACK_BARCODE>
<ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
<ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
<ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
<ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
<ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
<ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
</ns2:DW_PACK_BARCODESRECORDSELECT>
</ns2:SelectResult>
</ns2:SelectResponse>
</InputMessagePart_1>
</ns0:Root>
I'd like to select the value of the CHECK_DIGIT_PACK_BARCODE element that is a sibling of a given PRODUCT_ID
Following Tim's suggestion, here's my XSLT which attempts to provide a value for OuterCartonBarcode by keying from the CHECK_DIGIT_PACK_BARCODE of the ns2:SelectResponse
<?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 ns2 s1" version="1.0" xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster" xmlns:s1="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s1:Root" />
</xsl:template>
<xsl:template match="/s1:Root">
<ns1:ArrayOfArticleMasterDTO>
<xsl:for-each select="InputMessagePart_0/ns1:ArrayOfArticleMasterDTO/ArticleMasterDTO">
<ArticleMasterDTO>
<AltBarcodes>
<string>
<xsl:value-of select="AltBarcodes/string/text()" />
</string>
<xsl:value-of select="AltBarcodes/text()" />
</AltBarcodes>
<ClientId>
<xsl:value-of select="ClientId/text()" />
</ClientId>
<ArticleID>
<xsl:value-of select="ArticleID/text()" />
</ArticleID>
<ArticleReference>
<xsl:value-of select="ArticleReference/text()" />
</ArticleReference>
<ArticleSize>
<xsl:value-of select="ArticleSize/text()" />
</ArticleSize>
<ArticleStyle>
<xsl:value-of select="ArticleStyle/text()" />
</ArticleStyle>
<ArticleFit>
<xsl:value-of select="ArticleFit/text()" />
</ArticleFit>
<SupplierID>
<xsl:value-of select="SupplierID/text()" />
</SupplierID>
<Description>
<xsl:value-of select="Description/text()" />
</Description>
<Sku>
<xsl:value-of select="Sku/text()" />
</Sku>
<UnitsPerCase>
<xsl:value-of select="UnitsPerCase/text()" />
</UnitsPerCase>
<UnitsPerLayer>
<xsl:value-of select="UnitsPerLayer/text()" />
</UnitsPerLayer>
<xsl:variable name="var:v1" select="string(UnitsPerPallet/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v1)='true'">
<UnitsPerPallet>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</UnitsPerPallet>
</xsl:if>
<xsl:if test="string($var:v1)='false'">
<UnitsPerPallet>
<xsl:value-of select="UnitsPerPallet/text()" />
</UnitsPerPallet>
</xsl:if>
<xsl:variable name="var:v2" select="string(LayersPerPallet/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v2)='true'">
<LayersPerPallet>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</LayersPerPallet>
</xsl:if>
<xsl:if test="string($var:v2)='false'">
<LayersPerPallet>
<xsl:value-of select="LayersPerPallet/text()" />
</LayersPerPallet>
</xsl:if>
<xsl:variable name="var:v3" select="string(FullPalletWeightKG/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v3)='true'">
<FullPalletWeightKG>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</FullPalletWeightKG>
</xsl:if>
<xsl:if test="string($var:v3)='false'">
<FullPalletWeightKG>
<xsl:value-of select="FullPalletWeightKG/text()" />
</FullPalletWeightKG>
</xsl:if>
<xsl:variable name="var:v4" select="string(CaseLengthMM/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v4)='true'">
<CaseLengthMM>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</CaseLengthMM>
</xsl:if>
<xsl:if test="string($var:v4)='false'">
<CaseLengthMM>
<xsl:value-of select="CaseLengthMM/text()" />
</CaseLengthMM>
</xsl:if>
<xsl:variable name="var:v5" select="string(CaseHeightMM/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v5)='true'">
<CaseHeightMM>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</CaseHeightMM>
</xsl:if>
<xsl:if test="string($var:v5)='false'">
<CaseHeightMM>
<xsl:value-of select="CaseHeightMM/text()" />
</CaseHeightMM>
</xsl:if>
<CaseDepthMM>
<xsl:value-of select="CaseDepthMM/text()" />
</CaseDepthMM>
<CaseWeightG>
<xsl:value-of select="CaseWeightG/text()" />
</CaseWeightG>
<Colour>
<xsl:value-of select="Colour/text()" />
</Colour>
<Level1Description>
<xsl:value-of select="Level1Description/text()" />
</Level1Description>
<Level2Description>
<xsl:value-of select="Level2Description/text()" />
</Level2Description>
<Level3Description>
<xsl:value-of select="Level3Description/text()" />
</Level3Description>
<Level4Description>
<xsl:value-of select="Level4Description/text()" />
</Level4Description>
<xsl:call-template name="PBTemplate">
<xsl:with-param name="id" select="string(ArticleID/text())" />
</xsl:call-template>
<xsl:variable name="var:v6" select="string(PrimaryBarcode/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v6)='true'">
<PrimaryBarcode>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</PrimaryBarcode>
</xsl:if>
<xsl:if test="string($var:v6)='false'">
<PrimaryBarcode>
<xsl:value-of select="PrimaryBarcode/text()" />
</PrimaryBarcode>
</xsl:if>
</ArticleMasterDTO>
</xsl:for-each>
</ns1:ArrayOfArticleMasterDTO>
</xsl:template>
<xsl:template name="PBTemplate">
<xsl:param name="id" select="ArticleID" />
<xsl:for-each select="key('barcodes',$id)">
<OuterCartonBarcode>
<xsl:value-of select="/ns2:CHECK_DIGIT_PACK_BARCODE"/>
</OuterCartonBarcode>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Your current xpath ends with this...
/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE'[PRODUCT_ID/text()="100000005503"]]
... which does not look correct.
The expression you are looking for is this....
/ns0:Root/InputMessagePart_1/*[local-name() = 'SelectResponse']/*[local-name() = 'SelectResult']/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']
Or, to make it a bit more readable, this....
/ns0:Root
/InputMessagePart_1
/*[local-name() = 'SelectResponse']
/*[local-name() = 'SelectResult']
/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']
/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']
It would be much better if the expression did not try to ignore the namespaces, but used the relevant prefixes (as happens already with ns0:Root). Then the expression is much simplified
/ns0:Root/InputMessagePart_1/ns2:SelectResponse/ns2:SelectResult/*[ns2:PRODUCT_ID='100000005503']/ns2:CHECK_DIGIT_PACK_BARCODE
Or better still, define a key like so:
<xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />
Then you can write just this...
key('barcodes', '100000005503')/ns2:CHECK_DIGIT_PACK_BARCODE

XSLT 1.0 Grouping with multiple elements with same name

I have an XML that looks like -
<resultset>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2A:level3A</Hierarchy>
<Hierarchy>level1B:level2B:level3B</Hierarchy>
</ITEM>
</content>
</hit>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning1</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2A:level3A</Hierarchy>
</ITEM>
</content>
</hit>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning2</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2B:level3C</Hierarchy>
</ITEM>
</content>
</hit>
</resultset>
Note that there are multiple hierarchy elements which is a concatenated string of level1:level2:level3
I am looking to transform this into something like this -
<TREE>
<LEVELS>
<LEVEL1 name="level1A">
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM Name="Office Cleaning"/>
<ITEM Name="Office Cleaning1"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
<LEVEL2 name="level2B">
<LEVEL3 name="level3B">
<ITEM Name="Office Cleaning"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1A">
<LEVEL2 name="level2B">
<LEVEL3 name="level3C">
<ITEM Name="Office Cleaning2"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
</LEVELS>
</TREE>
Basically each item has multiple hierachy associated with it. I need to group them together.
I got only as far as -
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:autn="http://schemas.autonomy.com/aci/">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:key name="HIERARCHYLEVELS" match="resultset/hit/content/ITEM" use="HIERARCHY" />
<xsl:template match="/">
<TREE>
<xsl:for-each select="resultset/hit/content/ITEM[generate-id()=generate-id(key('HIERARCHYLEVELS', HIERARCHY)[1])]">
<xsl:for-each select="HIERARCHY">
<xsl:variable name="level" select="HIERARCHY"/>
<HIERARCHY name="{$level}" >
<xsl:variable name="name" select="TITLE"/>
<ITEM name="{$name}"/>
</HIERARCHY>
</xsl:for-each>
</xsl:for-each>
</TREE>
</xsl:template>
</xsl:stylesheet>
But the problem is I only get the first matching hierarchy tag. For e.g. I dont get to see "Office cleaning1".
What can I do to make sure all hierarchy elements are considered? I still need to split it into various levels.
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:strip-space elements="*"/>
<xsl:key name="kItemByHier" match="ITEM" use="Hierarchy"/>
<xsl:key name="kHierByVal" match="Hierarchy" use="."/>
<xsl:template match="/*">
<xsl:apply-templates select=
"*/*/*/Hierarchy[generate-id()=generate-id(key('kHierByVal',.)[1])]"/>
</xsl:template>
<xsl:template match="Hierarchy">
<xsl:call-template name="makeTree">
<xsl:with-param name="pHier" select="string()"/>
<xsl:with-param name="pItems" select="key('kItemByHier', .)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="makeTree">
<xsl:param name="pHier"/>
<xsl:param name="pDepth" select="1"/>
<xsl:param name="pItems" select="/.."/>
<xsl:choose>
<xsl:when test="not($pHier)">
<xsl:for-each select="$pItems">
<ITEM name="{TITLE}"/>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:element name="LEVEL{$pDepth}">
<xsl:attribute name="name">
<xsl:value-of select="substring-before(concat($pHier,':'), ':')"/>
</xsl:attribute>
<xsl:call-template name="makeTree">
<xsl:with-param name="pHier"
select="substring-after($pHier,':')"/>
<xsl:with-param name="pDepth" select="$pDepth+1"/>
<xsl:with-param name="pItems" select="$pItems"/>
</xsl:call-template>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<resultset>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2A:level3A</Hierarchy>
<Hierarchy>level1B:level2B:level3B</Hierarchy>
</ITEM>
</content>
</hit>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning1</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2A:level3A</Hierarchy>
</ITEM>
</content>
</hit>
<hit>
<content>
<ITEM>
<TITLE>Office Cleaning2</TITLE>
<DESCRIPTION>blah blah blah</DESCRIPTION>
<Hierarchy>level1A:level2B:level3C</Hierarchy>
</ITEM>
</content>
</hit>
</resultset>
produces the wanted, correct result:
<LEVEL1 name="level1A">
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM name="Office Cleaning"/>
<ITEM name="Office Cleaning1"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
<LEVEL2 name="level2B">
<LEVEL3 name="level3B">
<ITEM name="Office Cleaning"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1A">
<LEVEL2 name="level2B">
<LEVEL3 name="level3C">
<ITEM name="Office Cleaning2"/>
</LEVEL3>
</LEVEL2>
</LEVEL1>
For interest, here is a draft effort at a solution. It is close, but not quiet right, as you can see from the output, as it uses different grouping rules. I am still trying to understand the required grouping rules. I will update if I get a better understanding.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="xsl exsl">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="phase-1-output">
<xsl:apply-templates select="/" mode="phase-1" />
</xsl:variable>
<xsl:variable name="phase-2-output">
<xsl:apply-templates select="exsl:node-set($phase-1-output)" mode="phase-2" />
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select="$phase-2-output" />
</xsl:template>
<!--================ Phase 1 ===============================-->
<xsl:template match="/" mode="phase-1">
<t>
<xsl:apply-templates select="*/*/*/ITEM/Hierarchy" mode="phase-1" />
</t>
</xsl:template>
<xsl:template match="Hierarchy" mode="phase-1">
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="levels" select="." />
<xsl:with-param name="item" select="../TITLE" />
</xsl:call-template>
</xsl:template>
<xsl:template name="analyze-hierarchy"><!-- phase-1 -->
<xsl:param name="levels" />
<xsl:param name="item" />
<xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
<xsl:variable name="e-level" select="
translate(
substring($level,1,string-length($level) - 1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:choose>
<xsl:when test="$level">
<xsl:element name="{$e-level}">
<xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="levels" select="substring-after($levels,':')" />
<xsl:with-param name="item" select="$item" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<ITEM Name="{$item}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--================ Phase 2 ===============================-->
<xsl:key name="kLevel"
match="*[starts-with(name(),'LEVEL')]"
use="concat(generate-id(..),'|',#name)" />
<xsl:template match="/" mode="phase-2">
<TREE>
<LEVELS>
<xsl:variable name="t" select="concat(generate-id(t),'|')" />
<xsl:apply-templates select="t/LEVEL1[
generate-id() = generate-id( key('kLevel',concat($t,#name))[1])
]" mode="phase-2-head" />
</LEVELS>
</TREE>
</xsl:template>
<xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2-head">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates select="key('kLevel',concat(generate-id(..),'|',#name))" mode="phase-2" />
<xsl:copy-of select="ITEM" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2">
<xsl:variable name="p" select="concat(generate-id(.),'|')" />
<xsl:apply-templates select="*[starts-with(name(),'LEVEL')][
generate-id() = generate-id( key('kLevel',concat($p,#name))[1])
]" mode="phase-2-head" />
</xsl:template>
</xsl:stylesheet>
...with sample input produces this (not quiet correct output)...
<TREE>
<LEVELS>
<LEVEL1 name="level1A">
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM Name="Office Cleaning" />
</LEVEL3>
</LEVEL2>
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM Name="Office Cleaning1" />
</LEVEL3>
</LEVEL2>
<LEVEL2 name="level2B">
<LEVEL3 name="level3C">
<ITEM Name="Office Cleaning2" />
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
<LEVEL2 name="level2B">
<LEVEL3 name="level3B">
<ITEM Name="Office Cleaning" />
</LEVEL3>
</LEVEL2>
</LEVEL1>
</LEVELS>
</TREE>
UPDATE
Ok, round 2. I copied Dimitre's grouping rule, which is all or nothing on the content of the Hierarchy element. This solution produces the expected output for the sample input. Note that in contrast to Dimitre's <xsl:element name="LEVEL{$pDepth}"> method, I have derived the LEVEL1 style element names from the Hierarchy steps. I am not sure if this is correct.
<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:key name="kLevel" match="Hierarchy" use="." />
<xsl:template match="/">
<TREE>
<LEVELS>
<xsl:apply-templates select="*/*/*/ITEM/Hierarchy[
generate-id() = generate-id( key('kLevel',.)[1])
]" mode="group" />
</LEVELS>
</TREE>
</xsl:template>
<xsl:template match="Hierarchy" mode="group">
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="key" select="." />
<xsl:with-param name="levels" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="analyze-hierarchy">
<xsl:param name="key" />
<xsl:param name="levels" />
<xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
<xsl:variable name="e-level" select="
translate(
substring($level,1,string-length($level) - 1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:choose>
<xsl:when test="$level">
<xsl:element name="{$e-level}">
<xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="key" select="$key" />
<xsl:with-param name="levels" select="substring-after($levels,':')" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('kLevel',$key)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Hierarchy">
<ITEM Name="{../TITLE}" />
</xsl:template>
</xsl:stylesheet>

Extreme XSLT XML Flattening

I have the following input XML file:
<root>
<a>
<b>1</b>
</a>
<c>
<d>
<e>2</e>
<f>3</f> or <e>3</e>
</d>
<g h="4"/>
<i>
<j>
<k>
<l m="5" n="6" o="7" />
<l m="8" n="9" o="0" />
</k>
</j>
</i>
</c>
</root>
I would like to use XSLT to transform it into the follow outputs:
OUTPUT 1
<root>
<row b="1" e="2" f="3" h="4" m="5" n="6" o="7" />
<row b="1" e="2" f="3" h="4" m="8" n="9" o="0" />
<root>
OUTPUT 2
<root>
<row b="1" e="2" h="4" m="5" n="6" o="7" />
<row b="1" e="2" h="4" m="8" n="9" o="0" />
<row b="1" e="3" h="4" m="5" n="6" o="7" />
<row b="1" e="3" h="4" m="8" n="9" o="0" />
<root>
Can anyone help my XSLT isn't very strong. Thanks.
It will be easier if you let the occurrence of <e> determine the outer loop constructing your <row>s and have a inner loop iterating over all <l>s.
Try something 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="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="root">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="e">
<!-- store values of 'e' and 'f' in params -->
<xsl:param name="value_of_e" select="." />
<xsl:param name="value_of_f" select="ancestor::d[1]/f" />
<!-- iterate over all 'l's -->
<xsl:for-each select="//l">
<xsl:element name="row">
<xsl:attribute name="b">
<xsl:value-of select="//b" />
</xsl:attribute>
<xsl:attribute name="e">
<xsl:value-of select="$value_of_e" />
</xsl:attribute>
<!-- only include 'f' if it has a value -->
<xsl:if test="$value_of_f != ''">
<xsl:attribute name="f">
<xsl:value-of select="$value_of_f" />
</xsl:attribute>
</xsl:if>
<xsl:attribute name="h">
<xsl:value-of select="ancestor::c[1]/g/#h" />
</xsl:attribute>
<xsl:attribute name="m">
<xsl:value-of select="./#m" />
</xsl:attribute>
<xsl:attribute name="n">
<xsl:value-of select="./#n" />
</xsl:attribute>
<xsl:attribute name="o">
<xsl:value-of select="./#o" />
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="b" />
<xsl:template match="f" />
<xsl:template match="g" />
</xsl:stylesheet>

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'