I'd like to start by saying I'm not super good at XPATH and that is the main reason I come to you guys for some assistance.
So I was working today on trying to "group", or so to say, some data from an XML file based on both of them sharing an ID. I managed, with help from a friend, to do this but it was rather long winded and I'm certain there must be an easier/cleaner way. Below is the XML, XSLT I used and the desired output:
<Dude>
<ID>768</ID>
<Name>Mr Dude Man</Name>
</Dude>
...
<Basket>
<CustomerID>768</CustomerID>
<Purchases>
<PurchasedItem>
<ItemID>736383-2</ItemID>
<ItemName>XSLT Training</ItemName>
<ItemType>Book</ItemType>
<ItemQuantity>2</ItemQuantity>
</PurchasedItem>
<PurchasedItem>
<ItemID>736383-2</ItemID>
<ItemName>Candy</ItemName>
<ItemType>Consumable</ItemType>
<ItemQuantity>1</ItemQuantity>
</PurchasedItem>
</Purchases>
</Basket>
XSLT I used:
<xsl:apply-templates select="Dude"/>
<xsl:template match="Dude">
{Name} has purchased:
<xsl:apply-templates select="Basket[Basket/CustomerID = ../Dude/ID]"/>
</xsl:template>
<xsl:template match="Basket">
{ItemName}
</xsl:template>
In the above example, each Dude
can have a single basket and the basket has a customerID
connected to it to identify the basket owner. Assume that both nodes are as deep as each other. How would I go about, using xpath on an <apply-templates/>
, to produce the following result:
ps. Don't worry to much about the actual output, I just want to know the proper way of traversing an XML tree while matching on one of the nodes using apply-templates
Mr Dude Man has purchased: XSLT Training, Candy
EDIT: Forgot the XSLT I Used... Now the thing I am confused about is that is this the best way of doing this? With two separate matches. Also inside of a predicate do I need the ../
or does the predicate assume I'm starting at where I was matched eg: Dude
An efficient way to do cross-references like this is to use a key:
<xsl:key name='kBasket' match="Basket" use="CustomerID" />
Then you can do this:
<xsl:template match="/">
<xsl:apply-templates select="/Full/Path/To/Dude" />
</xsl:template>
<xsl:template match="Dude">
{Name} has purchased:
<xsl:apply-templates select="key('kBasket', ID)"/>
</xsl:template>
<xsl:template match="Basket">
<-- Any per-basket stuff could be output here -->
<xsl:apply-templates select="Purchases/PurchasedItem" />
</xsl:template>
<xsl:template match="PurchasedItem">
<xsl:value-of select="ItemName" />
</xsl:template>
Basket
(and you didn't have the requisite path to get to Basket
so the node-set was already empty at that point). The correct way to do that would be something like this:
<xsl:apply-templates select="/Absolute/Path/To/Basket[CustomerID = current()/ID]"/>
but the key approach is preferable because it is more efficient.