XSLT 1.0 - How to only copy elements with values, or descendents with values - xslt-1.0

With XML like
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Sku />
<Suppliers>
<Supplier>
<Name />
</Supplier>
</Suppliers>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
<e />
</Product>
</Products>
How to transform and only output if element has value, or descendent has a value?
(The 2nd 'filter template' filters empty, with match=*[not(node() )] , but only if no descendents)
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- When matching empty: do nothing -->
<xsl:template match="*[not(node())]">
<xsl:comment>filtering <xsl:value-of select="local-name()"/></xsl:comment>
</xsl:template>
Current output: (With debug-comments to identify names of filtered elements)
<?xml version="1.0" encoding="utf-8"?><Products>
<Product>
<!--filtering Sku-->
<Suppliers>
<Supplier>
<!--filtering Name-->
</Supplier>
</Suppliers>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
<!--filtering e-->
</Product>
</Products>
Required output: (without comments)
<?xml version="1.0" encoding="utf-8"?><Products>
<Product>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
</Product>
</Products>
Assume this requires a type of forward-look (recursive template?), to see if any descendents have a value, before copying element

Assuming that by "value" you mean a non whitespace-only text node, you could do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- prune branches that do not carry fruit -->
<xsl:template match="*[not(descendant::text())]"/>
</xsl:stylesheet>

Related

How to remove Namespaces when copying nodes to a different node in XML through XSLT1.0?

I'm new to XSL and i'm trying to copy a node of XML to another node in the same XML using XSLT. I can able to transform the file as expected but the XMLNS attribute is getting added to the destination which I don't want.
I have tried all the option of copy-namespaces='no' using XSLT2.0 but that doesn't work. Also, I cant have a prefix of namespaces in the XSLT and use exclude namespaces to avoid xmlns because the incoming XML file is dynamic and namespaces keep on changing. I can't have all the namespaces declared as a prefix in XSLT.
Incoming XML:
<?xml version="1.0" encoding="UTF-8"?>
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId></BusinessId>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName></SourceName>
<SourceId></SourceId>
<EventType></EventType>
<SecurityIdentifierId></SecurityIdentifierId>
<ClientRequestId></ClientRequestId>
<TargetId>ESB</TargetId>
<EventTime></EventTime>
<RequestUser></RequestUser>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId></ActivitySRId>
<ActivityTemplateId></ActivityTemplateId>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment></Comment>
<Type>Action</Type>
<ListOfDestination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</ListOfDestination>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName></DestinationName>
<DestinationId></DestinationId>
</Destination>
</DestinationList>
</Message>
</PropertySet>
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']//*[name()='Destination']"></xsl:template>
<!--Copy the destination from one node to another node-->
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']">
<xsl:copy>
<xsl:copy-of select="//*[name()='Message']/*[name()='EventList']//*[name()='Destination']"/>
</xsl:copy>
</xsl:template>
<!--Remove the original node from where the destination was copied-->
<xsl:template match="//*[name()='Message']/*[name()='EventList']//*[name()='ListOfDestination']"></xsl:template>
</xsl:stylesheet>
Output:
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
Expected Output:
Without the Namespaces on the copied node.
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
You are asking the wrong question. You don't want to "remove namespaces" - you want to change the namespace of transformed elements. This is akin to renaming the elements.
Now, since your example is confusing (and too long), consider the following:
XML
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes xmlns="some-unknown-namespace">
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
To change the namespace of shapes (and its descendants, that inherit it!) to no-namespace, without knowing in advance what the original namespace will be, you can do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor-or-self::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes>
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
Added:
lets modify the code to move the shapes into sizes and remove the shapes node.
OK, let's do that:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sizes">
<xsl:copy>
<xsl:apply-templates/>
<!-- move the shapes into sizes -->
<xsl:apply-templates select="../*[name()='shapes']/*"/>
</xsl:copy>
</xsl:template>
<!-- remove the shapes element -->
<xsl:template match="*[name()='shapes']"/>
<xsl:template match="*[ancestor::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<sizes>
<size>small</size>
<size>large</size>
<shape>circle</shape>
<shape>square</shape>
</sizes>
</root>

Rename tag and remove attribute

I have an XML document (generated by WiX heat) where I want to remove the root element name while removing the attribute. The source tree looks like this
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Wix>
I figured out how to rename the node, but this doesn't remove the unnecessary attribute xmlns.
<xsl:template match='/wix:Wix'>
<Include>
<xsl:copy-of select="#*|node()"/>
</Include>
</xsl:template>
<!-- Even this template doesn't suppress the attribute xmlns -->
<xsl:template match='#xmlns'/>
I event removed the #*| from the select clause. But this doesn't have any effect.
How can I produce with XSLT 1.0 the following desired output?
<Include>
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Include>
this doesn't remove the unnecessary attribute xmlns.
xmlnsis not an attribute - it's a namespace, a part of the node's name. If you don't want it in the output, you cannot copy the input nodes that are in a namespace - you must create new nodes instead, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
exclude-result-prefixes="wix">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/wix:Wix">
<Include>
<xsl:apply-templates/>
</Include>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Get only products from xml file where there are no items under that product

I have an xml file which has products and items. Products may or may not have items. I want to get only those Products where there is no item.
This is my input
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<prod-id>P16753</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<items/>
<created-on>2016-08-12T11:30:00</created-on>
<created-by>Administrator</created-by>
<last-changed-on>2016-08-04T17:34:00</last-changed-on>
<last-changed-by>ap0712</last-changed-by>
<delete>false</delete>
</product>
<product>
<prod-id>P16754</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<items>
<item>
<item-id>i16754</item-id>
<item-status>CREATED</item-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
</item>
</items>
<created-on>2016-08-12T11:30:00</created-on>
<created-by>Administrator</created-by>
<last-changed-on>2016-08-04T17:34:00</last-changed-on>
<last-changed-by>ap0712</last-changed-by>
<delete>false</delete>
</product>
</products>
I want my output like this in the xml file. If possible, I want the tags to be removed so that I Can convert into csv file later
prod-id P16754
I have an xslt code(which I got from this forum) but I am not able to make this work
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<ns:WhiteList>
<name>prod-id</name>
</ns:WhiteList>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[not(descendant-or-self::*[name()=prod-id[not(items/item)]document('')/*/ns:WhiteList/*])]"/>
</xsl:transform>
If you use
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="products/product[not(items/item)]/prod-id/concat(local-name(), ' ', .)" separator="
"/>
</xsl:template>
</xsl:transform>
then for your input sample the output is
prod-id P16753
Online sample is at http://xsltransform.net/pPJ8LUP.

XSLT attribute based on value in Lookup Table created dynamically in XSLT over multiple XML input files

I need to take a value dynamically created by the position in a lookup table and assign it to an element created in the same transform, after traversing multiple files. I have a "manifest" xml file like below:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<files>
<file>file1.xml</file>
<file>file2.xml</file>
</files>
File 1 and File 2 are as follows:
File 1:
<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
<Product>
<Name>Schwinn Bicycle</Name>
<Type>Bicycle</Type>
<Quantity>2</Quantity>
<Store>
<Name>Some Store</Name>
<Location>Somewhere</Location>
<Contact>Phone Number</Contact>
</Store>
</Product>
<Product>
<Name>Huffy Bicycle</Name>
<Type>Bicycle</Type>
<Quantity>10</Quantity>
<Store>
<Name>Some Other Store</Name>
<Location>Somewhere Else</Location>
<Contact>Another Phone Number</Contact>
</Store>
</Product>
</ProductList>
File 2:
<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
<Product>
<Name>Expensive Bicycle Brand</Name>
<Type>Bicycle</Type>
<Quantity>2</Quantity>
<Store>
<Name>Yet Another Store</Name>
<Location>Whole New Place</Location>
<Contact>Completely Different Phone Number</Contact>
</Store>
</Product>
<Product>
<Name>Harley Davidson</Name>
<Type>Motorcycle</Type>
<Quantity>1</Quantity>
<Store>
<Name>Some Other Store</Name>
<Location>Somewhere Else</Location>
<Contact>Another Phone Number</Contact>
</Store>
</Product>
</ProductList>
Now what I need is for the file XML output to be as follows:
<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
<Stores>
<Store id="1" Name="Some Store">Phone Number</Store>
<Store id="2" Name="Some Other Store">Another Phone Number</Store>
<Store id="3" Name="Yet Another Store">Completely Different Phone Number</Store>
</Stores>
<Products>
<Product>
<Name>Huffy Bicycle</Name>
<StoreContact contactId="2"/>
</Product>
<Product>
<Name>Schwinn Bicycle</Name>
<StoreContact contactId="1"/>
</Product>
<Product>
<Name>Expensive Bicycle Brand</Name>
<StoreContact contactId="3"/>
</Product>
<Product>
<Name>Harly Davidson</Name>
<StoreContact contactId="2"/>
</Product>
</Products>
So, to sum it up, I am going through the files and getting the contact information out and using their position to make the lookup table at the top for the resulting XML. I then create the product elements for the output and need to somehow get the id that was assigned when I created the lookup table. I need to use pure XSLT 1.0.
Edit: The Id in the lookup table must be an integer value.
Here is what I have:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="UTF-8"/>
<xsl:variable name="input-docs" select="document(files/file)"/>
<xsl:variable name="store_contacts" select="document(files/file)/ProductList/Product/Store/Contact"/>
<xsl:template name="ProductList">
<xsl:for-each select="$input-docs">
<xsl:apply-templates select="Product"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="Contacts">
<xsl:element name="contacts">
<xsl:for-each select="$store_contacts">
<xsl:if test="generate-id()=generate-id($store_contacts[normalize-space(Contact)=normalize-space(current()/Contact)][1])">
<xsl:element name="Store">
<xsl:attribute name="id"><xsl:value-of select="position()"/></xsl:attribute>
<xsl:value-of select="normalize-space(Contact)"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:element name="ProductList">
<xsl:element name="Stores">
<xsl:call-template name="Contacts"/>
</xsl:element>
<xsl:element name="Products">
<xsl:call-template name="ProductList"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="Product">
<xsl:element name="StoreContact">
<xsl:attribute name="Name">
<xsl:value-of select="Name"/>
</xsl:attribute>
<xsl:attribute name="contactId">1
<!-- lookup value from the table created from all of the files, using the value 1 until I can figure out how to set the correct value -->
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I apologize if this is a duplicate question, I have tried to find something similar but haven't found any questions on the subject when the op was trying to perform the function while iterating over multiple files.
I understand from your previous question that you cannot use the node-set() extension function - which makes this very difficult.
I would suggest that, instead of position, you use the unique ids generated by the generate-id() function to link products to their stores.
The following stylesheet uses the method proposed by G. Ken Holman to produce a list of distinct stores gathered from multiple files. We then use the same principle to retrieve the unique id of each product's store:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="input-docs" select="document(files/file)"/>
<xsl:variable name="stores" select="$input-docs/ProductList/Product/Store"/>
<xsl:template match="/">
<ProductList>
<Stores>
<xsl:for-each select="$stores">
<xsl:if test="generate-id()=generate-id($stores[Contact=current()/Contact][1])">
<Store id="{generate-id()}" Name="{Name}">
<xsl:value-of select="Contact"/>
</Store>
</xsl:if>
</xsl:for-each>
</Stores>
<Products>
<xsl:for-each select="$input-docs/ProductList/Product">
<Product>
<xsl:copy-of select="Name"/>
<StoreContact contactId="{generate-id($stores[Contact=current()/Store/Contact][1])}"/>
</Product>
</xsl:for-each>
</Products>
</ProductList>
</xsl:template>
</xsl:stylesheet>
The actual result can vary slightly according to the processor - here's an example produced by Saxon 6.5:
<?xml version="1.0" encoding="UTF-8"?>
<ProductList>
<Stores>
<Store id="d1e14" Name="Some Store">Phone Number</Store>
<Store id="d1e38" Name="Some Other Store">Another Phone Number</Store>
<Store id="d2e14" Name="Yet Another Store">Completely Different Phone Number</Store>
</Stores>
<Products>
<Product>
<Name>Schwinn Bicycle</Name>
<StoreContact contactId="d1e14"/>
</Product>
<Product>
<Name>Huffy Bicycle</Name>
<StoreContact contactId="d1e38"/>
</Product>
<Product>
<Name>Expensive Bicycle Brand</Name>
<StoreContact contactId="d2e14"/>
</Product>
<Product>
<Name>Harley Davidson</Name>
<StoreContact contactId="d1e38"/>
</Product>
</Products>
</ProductList>
Here is a complete solution that produces meaningful integer Ids:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vAllDocs" select="document(/*/*)"/>
<xsl:variable name="vAllStores" select="$vAllDocs/*/*/Store"/>
<xsl:variable name="vPositionsOfDistinct">
<xsl:for-each select="$vAllStores">
<xsl:variable name="vCur" select="."/>
<xsl:if test=
"generate-id()
= generate-id($vAllStores[concat(Name,'|',Location,'|',Contact)
= concat($vCur/Name,'|',$vCur/Location,'|',$vCur/Contact)]
[1])">
<xsl:value-of select="concat('|', position())"/>
</xsl:if>
</xsl:for-each>
<xsl:text>|</xsl:text>
</xsl:variable>
<xsl:variable name="vDistinctStores" select=
"$vAllStores[contains($vPositionsOfDistinct, concat('|',position(),'|'))]"/>
<xsl:template match="/">
<ProductList>
<Stores>
<xsl:apply-templates select="$vAllStores" mode="distinct"/>
</Stores>
<Products><xsl:apply-templates select="$vAllDocs/*/Product"/></Products>
</ProductList>
</xsl:template>
<xsl:template match="Store" mode="distinct">
<xsl:if test="contains($vPositionsOfDistinct, concat('|',position(),'|'))">
<Store id="{position()}" Name="{Name}"><xsl:value-of select="Contact"/></Store>
</xsl:if>
</xsl:template>
<xsl:template match="Product">
<xsl:variable name="vCur" select="Store"/>
<xsl:variable name="vPos">
<xsl:for-each select="$vAllStores">
<xsl:if test=
"contains($vPositionsOfDistinct, concat('|',position(),'|'))
and
concat(Name,'|',Location,'|',Contact)
= concat($vCur/Name,'|', $vCur/Location,'|', $vCur/Contact)
">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<Product>
<xsl:copy-of select="Name"/>
<StoreContact contactId="{$vPos}"/>
</Product>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<files>
<file>afile1.xml</file>
<file>afile2.xml</file>
</files>
and on the provided two XML files (two products reordered to show the more interesting case when the resulting store ids are not consecutive integers):
afile1.xml:
<ProductList>
<Product>
<Name>Schwinn Bicycle</Name>
<Type>Bicycle</Type>
<Quantity>2</Quantity>
<Store>
<Name>Some Store</Name>
<Location>Somewhere</Location>
<Contact>Phone Number</Contact>
</Store>
</Product>
<Product>
<Name>Huffy Bicycle</Name>
<Type>Bicycle</Type>
<Quantity>10</Quantity>
<Store>
<Name>Some Other Store</Name>
<Location>Somewhere Else</Location>
<Contact>Another Phone Number</Contact>
</Store>
</Product>
</ProductList>
afile2.xml:
<ProductList>
<Product>
<Name>Expensive Bicycle Brand</Name>
<Type>Bicycle</Type>
<Quantity>2</Quantity>
<Store>
<Name>Some Other Store</Name>
<Location>Somewhere Else</Location>
<Contact>Another Phone Number</Contact>
</Store>
</Product>
<Product>
<Name>Harley Davidson</Name>
<Type>Motorcycle</Type>
<Quantity>1</Quantity>
<Store>
<Name>Yet Another Store</Name>
<Location>Whole New Place</Location>
<Contact>Completely Different Phone Number</Contact>
</Store>
</Product>
</ProductList>
Then the wanted, correct result is produced:
<ProductList>
<Stores>
<Store id="1" Name="Some Store">Phone Number</Store>
<Store id="2" Name="Some Other Store">Another Phone Number</Store>
<Store id="4" Name="Yet Another Store">Completely Different Phone Number</Store>
</Stores>
<Products>
<Product>
<Name>Schwinn Bicycle</Name>
<StoreContact contactId="1"/>
</Product>
<Product>
<Name>Huffy Bicycle</Name>
<StoreContact contactId="2"/>
</Product>
<Product>
<Name>Expensive Bicycle Brand</Name>
<StoreContact contactId="2"/>
</Product>
<Product>
<Name>Harley Davidson</Name>
<StoreContact contactId="4"/>
</Product>
</Products>
</ProductList>

XSLT get Node by Index

i am beginner in XSLT and i am using it to transform XML to XML
This is the source XML i receive
Source XML:
<Response>
<Pax>
<Id>1</Id>
</Pax>
<Pax>
<Id>2</Id>
</Pax>
<Travelers>
<Traveler>
<Name>ABC</Name>
</Traveler>
<Traveler>
<Name>XYZ</Name>
</Traveler>
</Travelers>
</Response>
I have written below XSLT
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="Response">
<xsl:element name="Root">
<xsl:apply-templates select="Travelers/Traveler"/>
</xsl:element>
</xsl:template>
<xsl:template match="Traveler">
<xsl:element name="Person">
<xsl:element name="PId">
<xsl:value-of select="//Pax/Id[position()]" />
</xsl:element>
<xsl:element name="Name">
<xsl:value-of select="Name" />
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>1</PId>
<Name>XYZ</Name>
</Person>
</Root>
I would like to generate below XML output
Expected Output:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>2</PId>
<Name>XYZ</Name>
</Person>
</Root>
As shown in above XML the only issue is with PId, it should have value 2.
Please help. Thanks.
Here's a relatively simple solution.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<Root>
<xsl:apply-templates select="Pax" />
</Root>
</xsl:template>
<xsl:template match="Pax">
<xsl:variable name="vPosition" select="position()" />
<Person>
<PId>
<xsl:value-of select="Id" />
</PId>
<Name>
<xsl:value-of select="/*/Travelers/*[$vPosition]/Name" />
</Name>
</Person>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML:
<Response>
<Pax>
<Id>1</Id>
</Pax>
<Pax>
<Id>2</Id>
</Pax>
<Travelers>
<Traveler>
<Name>ABC</Name>
</Traveler>
<Traveler>
<Name>XYZ</Name>
</Traveler>
</Travelers>
</Response>
...the wanted result is produced:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>2</PId>
<Name>XYZ</Name>
</Person>
</Root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="Response">
<Root>
<xsl:for-each select="Travelers/Traveler">
<Person>
<xsl:variable name="index" select="position()" />
<Pid><xsl:value-of select="//Pax[$index]/Id"/></Pid>
<Name><xsl:value-of select="Name"/></Name>
</Person>
</xsl:for-each>
</Root>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="/Response">
<Root>
<xsl:for-each select="Pax">
<xsl:variable name="pos" select="position()"/>
<Person>
<PId>
<xsl:value-of select="Id"/>
</PId>
<xsl:apply-templates select="//Travelers">
<xsl:with-param name="pos" select="$pos"/>
</xsl:apply-templates>
</Person>
</xsl:for-each>
</Root>
</xsl:template>
<xsl:template match="Travelers">
<xsl:param name="pos"/>
<xsl:for-each select="//Name">
<xsl:if test="position()=$pos">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>