DJ1
DJ1

Reputation: 11

How to restrict key() to search within a child node and not entire xml

Is there any way in XSLT 1.0 to restrict the function key() to search within a child node and not entire input XML. My input XML is

    <a>
    <b>
        <c>
            <d name="1"/>
            <d name="2"/>
            <d name="3"/>
            <d name="1"/>
        </c>
    </b>
    <b>
        <c>
            <d name="1"/>
            <d name="2"/>
        </c>
    </b>
</a>

XSLT that I have written is

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:key name="findD" match="d" use="@name"/>

<xsl:template match="/">
    <start>
        <xsl:apply-templates select="a/b/c"/>
    </start>
</xsl:template>

<xsl:template match="a/b/c">
    <inside>
        <xsl:for-each select="key('findD', '1')">
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </inside>
</xsl:template>

Which results in

    <?xml version='1.0' ?>
<start>
    <inside>
        <d name="1"/>
        <d name="1"/>
        <d name="1"/>
    </inside>
    <inside>
        <d name="1"/>
        <d name="1"/>
        <d name="1"/>
    </inside>
</start> 

What I need is something like

     <?xml version='1.0' ?>
<start>
    <inside>
        <d name="1"/>
        <d name="1"/>
    </inside>
    <inside>
        <d name="1"/>
    </inside>
</start> 

This returns all the three nodes 'd' (present in xml) for every c node. While what I need is to get d nodes within corresponding c node (when key is called).

I have seen use of concat within key() where one can concat result based on node c's value. Is there any other way to say key() to look within a node only and not entire XML.

Upvotes: 1

Views: 163

Answers (1)

John Ernst
John Ernst

Reputation: 1235

Here is an answer:

<xsl:key name="findD" match="d" use="generate-id(..)"/>

<xsl:template match="/">
  <start>
    <xsl:apply-templates select="a/b/c"/>
  </start>
</xsl:template>

<xsl:template match="a/b/c">
  <xsl:variable name="parentID" select="generate-id(.)"/>
  <inside>
    <xsl:for-each select="key('findD', $parentID)[@name='1']">
      <xsl:copy-of select="."/>
    </xsl:for-each>
  </inside>
</xsl:template>

Please tell us why you want to use a key here. Just using normal push programming would be a very efficient and straight forward way to solve this problem.

<xsl:template match="/">
  <start>
    <xsl:apply-templates select="a/b/c"/>
  </start>
</xsl:template>

<xsl:template match="c">
  <inside>
    <xsl:apply-templates select="d[@name='1']"/>
  </inside>
</xsl:template>

<xsl:template match="d">
  <xsl:copy-of select="."/>
</xsl:template>

Upvotes: 0

Related Questions