Getting two xml elements sepearted by comma in the same row - xslt-1.0

I want an xslt file which operates on xml file to get two prod-id and article-number in one line separated by comma. This should happen only where
Product has no items.
My input xml file is:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<prod-id>P16653</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<article-number>233060</article-number>
<languagedata>
<language>
<languageid>en-GB</languageid>
<buying-description>FENDI L VELOUR SWEAT PANTS BEIGE</buying-description>
<customer-long-description>FENDI L VELOUR SWEAT PANTS BEIGE</customer-long-description>
</language>
</languagedata>
<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>P16659</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<article-number>345940</article-number>
<languagedata>
<language>
<languageid>en-GB</languageid>
<buying-description>FENDI L VELOUR SWEAT PANTS BEIGE</buying-description>
<customer-long-description>FENDI L VELOUR SWEAT PANTS BEIGE</customer-long-description>
</language>
</languagedata>
<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>P16755</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<article-number>11060</article-number>
<languagedata>
<language>
<languageid>en-GB</languageid>
<buying-description>FENDI L VELOUR SWEAT PANTS BEIGE</buying-description>
<customer-long-description>FENDI L VELOUR SWEAT PANTS BEIGE</customer-long-description>
</language>
</languagedata>
<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>
<product>
<prod-id>P16633</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<article-number>21069</article-number>
<languagedata>
<language>
<languageid>en-GB</languageid>
<buying-description>FENDI L VELOUR SWEAT PANTS BEIGE</buying-description>
<customer-long-description>FENDI L VELOUR SWEAT PANTS BEIGE</customer-long-description>
</language>
</languagedata>
<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>P16785</prod-id>
<product-status>CREATED</product-status>
<validation-status>Valid</validation-status>
<duplication-status>Unique</duplication-status>
<content-status>New</content-status>
<article-number>9881061</article-number>
<languagedata>
<language>
<languageid>en-GB</languageid>
<buying-description>FENDI L VELOUR SWEAT PANTS BEIGE</buying-description>
<customer-long-description>FENDI L VELOUR SWEAT PANTS BEIGE</customer-long-description>
</language>
</languagedata>
<items>
<item>
<item-id>i16757</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 output as
P16653,233060
P16659,345940
I am using the following xslt which I got from this forum and tried to modify it but it is giving me only article-number and not both
<?xml version="1.0" encoding="UTF-8"?>
<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:value-of select="products/product[not(items/item)]/article-number/concat(local-name(), ' ', .)" separator="
"/>
</xsl:template>
</xsl:transform>

When I use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="products/product[not(items/item)]/concat(prod-id, ',', article-number)" separator="
"/>
</xsl:template>
</xsl:stylesheet>
with an XSLT 2.0 processor like Saxon 9 I get
P16653,233060
P16659,345940
P16633,21069

Why don't you do simply:
<xsl:template match="/products">
<xsl:for-each select="product[not(items/item)]">
<xsl:value-of select="prod-id"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="article-number"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>

Related

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

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>

Find distinct values across each node in xslt1.0

I want to find distinct values across each node. but when i use muenchian method it will select distinct values only at top level.
Currently i am getting output as:
Bag 20
Tray 30
But i want to group type and quantity for each item
<!--for 1st item-->
Bag 20
Tray 30
<!--for 2nd item-->
Bag 20
Box 20
Tray 30
I am doing some research to make it work but did not succeed.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Box</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
</Test>
Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"
extension-element-prefixes="date"
>
<xsl:key name="item-key" match="Items/Item[name='A']" use="input/#quantity" />
<xsl:template match="/" name="Barcode">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="ManufacturLabelSize-first" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm" />
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:simple-page-master master-name="ManufacturLabelSize-rest" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm"/>
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:page-sequence-master master-name="ManufacturLabelSize-portrait">
<fo:repeatable-page-master-alternatives>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-first"
page-position="first"/>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-rest"
page-position="rest"/>
</fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="ManufacturLabelSize-portrait" id="pSeqID">
<fo:flow flow-name="xsl-region-body">
<fo:table >
<fo:table-body border="solid" border-width="0.5pt">
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:for-each select="Test">
<xsl:for-each select="Items">
<xsl:for-each select="Item[name='A'][count(. | key('item-key',input/#quantity)[1])=1]">
<fo:block>
<xsl:value-of select="type"/> <fo:inline color="white">XXX</fo:inline><xsl:value-of select="input/#quantity"/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Appreciate your help!
If - as it seems - you want to find distinct Items whose name is A by their type separately for each Items, then you need to include the parent Items' id in the key. Here's a simplified example:
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:key name="k" match="Item[name='A']" use="concat(type, '|', generate-id(..))"/>
<xsl:template match="/Test">
<root>
<xsl:for-each select="Items">
<xsl:comment>
<xsl:value-of select="position()" />
</xsl:comment>
<xsl:for-each select="Item[name='A'][count(. | key('k', concat(type, '|', generate-id(..)))[1]) = 1]">
<item>
<type>
<xsl:value-of select="type"/>
</type>
<quantity>
<xsl:value-of select="input/#quantity"/>
</quantity>
</item>
</xsl:for-each>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!--1-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
<!--2-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Box</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
</root>

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>

Using XSLT to traverse muliple nodes and to select by attribute name

I have the following XML format below, where my main repeating node is
<portfolioSummary>...</portfolioSummary>
One of the descendent nodes is
<keys>
<key>...</key>
<key>...</key>
</keys>
and as you can see, there are multiple <key> child nodes.
I would like to set up an XSLT template whereby I'm selecting all <portfolioSummary> nodes where keys/key[displayValue='HSVaR'] .
My XML input sample set is :
<?xml version="1.0" encoding="UTF-8"?>
<outBound>
<body>
<portfolioSummary portfolioId="61">
<portfolio id="42">
<currency>USD</currency>
<keys>
<key displayValue="My Company Inc" sequenceValue="My Company Inc" sequenceNumber="30" value="My Company Inc" />
<key displayValue="COUNTERPARTY IRS" sequenceValue="COUNTERPARTY IRS" sequenceNumber="50" value="COUNTERPARTY IRS" />
<key displayValue="Historical VaR" sequenceNumber="330" value="HSVaR" />
</keys>
</portfolio>
<exposureProfile>
<node date="2008-08-08">
<tag>HSVaR 5D 99.7 ES</tag>
<exposure>16250079</exposure>
</node>
</exposureProfile>
<summary>
<util>33000000</util>
</summary>
</portfolio>
</portfolioSummary>
</body>
</outBound>
My desired OUTPUT with sample is (where <extIA>..</extIA> may be repeated many times over) :
<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extIA>
<legal_id>My Company Inc</legal_id>
<AMOUNT>16250079</AMOUNT>
<CURRENCY>USD</CURRENCY>
<ValuationDate>2008-08-08</ValuationDate>
<externalSystem>myExternalSystem123</externalSystem>
</extIA>
<extIA>
<legal_id>My Company Inc</legal_id>
<AMOUNT>100000</AMOUNT>
<externalSystem>myExternalSystem123</externalSystem>
</extIA>
</collection>
and my XSLT starter code is the following (just some starter ideas):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
version="1.0"
indent="yes">
<xsl:template match="/*">
<collection>
<xsl:apply-templates select="/outbound/body/portfolioSummary"/>
</collection>
</xsl:template>
<!-- Pull portfolioSummary nodes -->
<xsl:template match="portfolio/keys/key[#value='HSVAR']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
<extIA>
<legal_id><xsl:value-of select="#portfolioId"></xsl:value-of></legal_id>
<PRODUCT><xsl:value-of select="summary"/></PRODUCT>
<AMOUNT><xsl:value-of select="exposureProfile"></xsl:value-of></AMOUNT>
<CURRENCY>USD</CURRENCY>
<ValuationDate>2012-05-15</ValuationDate>
<externalSystem>My External System</externalSystem>
</extIA>
</xsl:template>
</xsl:stylesheet>
Again, I would be needing to pull all of those <portfolioSummary> nodes, but ONLY where portfolio/keys/key[#value='HSVAR'] .
Your advice would be greatly appreciated.
I would like to take a guess here. Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<outBound>
<body>
<portfolioSummary portfolioId="42">
<portfolio id="42">
<currency>USD</currency>
<keys>
<key displayValue="My Company Inc" sequenceValue="My Company Inc" sequenceNumber="30" value="My Company Inc" />
<key displayValue="COUNTERPARTY IRS" sequenceValue="COUNTERPARTY IRS" sequenceNumber="50" value="COUNTERPARTY IRS" />
<key displayValue="Historical VaR" sequenceNumber="330" value="HSVaR" />
</keys>
<exposureProfile>
<node date="2008-08-08">100000</node>
</exposureProfile>
<summary>
<util>33000000</util>
</summary>
</portfolio>
</portfolioSummary>
</body>
</outBound>
and the following stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<collection>
<xsl:apply-templates select="/outBound/body/portfolioSummary[descendant::key/#value[.='HSVaR']]"/>
</collection>
</xsl:template>
<xsl:template match="portfolioSummary">
<extIA>
<legal_id><xsl:value-of select="#portfolioId"/></legal_id>
<PRODUCT><xsl:value-of select="descendant::summary/util"/></PRODUCT>
<AMOUNT><xsl:value-of select="descendant::exposureProfile/node"/></AMOUNT>
<CURRENCY><xsl:value-of select="descendant::currency"/></CURRENCY>
<ValuationDate><xsl:value-of select="descendant::exposureProfile/node/#date"/></ValuationDate>
<externalSystem>RAZOR</externalSystem>
</extIA>
</xsl:template>
</xsl:stylesheet>
it outputs:
<?xml version="1.0" encoding="utf-8"?>
<collection>
<extIA>
<legal_id>42</legal_id>
<PRODUCT>33000000</PRODUCT>
<AMOUNT>100000</AMOUNT>
<CURRENCY>USD</CURRENCY>
<ValuationDate>2008-08-08</ValuationDate>
<externalSystem>RAZOR</externalSystem>
</extIA>
</collection>