I need to generate integer IDs for products and than reference related products by those integer IDs in the output. On the input I have string keys representing this relationship. Thank you for your help.
Input:
<root>
<products>
<product>
<!-- a unique string key of this node between the other product nodes -->
<stringKey>AppleRef</stringKey>
<Name>Apple</Name>
<relatedProducts>
<!-- a reference to product/StringKey of Orange -->
<relatedProductStringKey>OrangeRef</relatedProductStringKey>
<!-- other related products may follow -->
</relatedProducts>
</product>
<product>
<stringKey>OrangeRef</stringKey>
<Name>Orange</Name>
<relatedProducts>
<relatedProductStringKey>AppleRef</relatedProductStringKey>
</relatedProducts>
</product>
</products>
</root>
Expected output:
<root>
<products>
<P>
<ProductInfo>
<!-- a unique integer ID of this node between the other ProductsInfo nodes -->
<ProductID>0</ProductID>
<ProductRef>AppleRef</ProductRef>
<ProductName>Apple</ProductName>
</ProductInfo>
<R>
<ProductRelatedInfo>
<!-- a unique integer ID of this node between the other ProductRelatedInfo nodes -->
<RelatedID>0</RelatedID>
<!-- a reference to ProductInfo/ProductID of Orange -->
<RelatedProductID>1</RelatedProductID>
</ProductRelatedInfo>
<!-- other related products may follow -->
</R>
</P>
<P>
<ProductInfo>
<ProductID>1</ProductID>
<ProductRef>OrangeRef</ProductRef>
<ProductName>Orange</ProductName>
</ProductInfo>
<R>
<ProductRelatedInfo>
<RelatedID>1</RelatedID>
<RelatedProductID>0</RelatedProductID>
</ProductRelatedInfo>
</R>
</P>
</products>
</root>
This is easy to do using a key. For example, the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="product" match="product" use="stringKey" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stringKey">
<ProductID><xsl:value-of select="count(../preceding-sibling::product)"/></ProductID>
<ProductRef><xsl:value-of select="."/></ProductRef>
</xsl:template>
<xsl:template match="relatedProductStringKey">
<RelatedProductID><xsl:value-of select="count(key('product', .)/preceding-sibling::product)"/></RelatedProductID>
</xsl:template>
</xsl:stylesheet>
when applied to your input, will return:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<products>
<product>
<ProductID>0</ProductID>
<ProductRef>AppleRef</ProductRef>
<Name>Apple</Name>
<relatedProducts>
<RelatedProductID>1</RelatedProductID>
</relatedProducts>
</product>
<product>
<ProductID>1</ProductID>
<ProductRef>OrangeRef</ProductRef>
<Name>Orange</Name>
<relatedProducts>
<RelatedProductID>0</RelatedProductID>
</relatedProducts>
</product>
</products>
</root>
If you prefer a meaningless, though not necessarily numeric ID, you might prefer the simpler:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="product" match="product" use="stringKey" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stringKey">
<ProductID><xsl:value-of select="generate-id(..)"/></ProductID>
<ProductRef><xsl:value-of select="."/></ProductRef>
</xsl:template>
<xsl:template match="relatedProductStringKey">
<RelatedProductID><xsl:value-of select="generate-id(key('product', .))"/></RelatedProductID>
</xsl:template>
</xsl:stylesheet>
The exact result depends on the processor, for example Saxon might return:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<products>
<product>
<ProductID>d0e3</ProductID>
<ProductRef>AppleRef</ProductRef>
<Name>Apple</Name>
<relatedProducts>
<RelatedProductID>d0e11</RelatedProductID>
</relatedProducts>
</product>
<product>
<ProductID>d0e11</ProductID>
<ProductRef>OrangeRef</ProductRef>
<Name>Orange</Name>
<relatedProducts>
<RelatedProductID>d0e3</RelatedProductID>
</relatedProducts>
</product>
</products>
</root>
while libxslt will produce something like:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<products>
<product>
<ProductID>idp116928</ProductID>
<ProductRef>AppleRef</ProductRef>
<Name>Apple</Name>
<relatedProducts>
<RelatedProductID>idp1506944</RelatedProductID>
</relatedProducts>
</product>
<product>
<ProductID>idp1506944</ProductID>
<ProductRef>OrangeRef</ProductRef>
<Name>Orange</Name>
<relatedProducts>
<RelatedProductID>idp116928</RelatedProductID>
</relatedProducts>
</product>
</products>