XSLT: Get all children of parents with specified attributes

1k Views Asked by At

Given this XML:

<?xml version="1.0" encoding="iso-8859-2" ?>
<r>
    <products start="5" stop="8">
        <p>
            <id> 50 </id>
            <name> Murphy </name>
            <price> 33 </price>
        </p>
        <p>
            <id> 10 </id>
            <name> Margarethe </name>
            <price> 11 </price>
        </p>
    </products>
    <products start="1" stop="4">
        <p>
            <id> 555 </id>
            <name> XXXX</name>
            <price> 333 </price>
        </p>
        <p>
            <id> 10 </id>
            <name> Mexico </name>
            <price> 11 </price>
        </p>
    </products>
    <products start="1" stop="4">
        <p>
            <id> 30 </id>
            <name> HKoney </name>
            <price> 11 </price>
        </p>
        <p>
            <id> 10 </id>
            <name> HMargarethe </name>
            <price> 11 </price>
        </p>
    </products>
</r>

With this XSL:

<?xml version="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:template match="products[@start &lt; 2 and @stop &gt; 2]" >
    <xsl:call-template name="test"/>
</xsl:template>

<xsl:template name="test">
     <test><xsl:value-of select="*/name"/></test>
</xsl:template>

<xsl:template match="products"/>

</xsl:stylesheet>

Having this output:

<?xml version="1.0" encoding="UTF-8"?>
<test> XXXX</test>
<test> HKoney </test>

Q : Why am I not getting all the <p> with parent of specified attributes, only the first one? Mexico and HMargarethe was expected also.


Desired output:

<?xml version="1.0" encoding="UTF-8"?>
<test> XXXX</test>
<test> Mexico </test>
<test> HKoney </test>
<test> HMargerethe</test>

NOTE: I wish to have it working without for-each loop.

4

There are 4 best solutions below

0
On BEST ANSWER

you can try using apply-templates directly on the <name> element:

<?xml version="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:template match="products[@start &lt; 2 and @stop &gt; 2]" >
        <xsl:apply-templates select="p/name"/>
    </xsl:template>

    <xsl:template match="name">
        <test>
            <xsl:value-of select="."/>
        </test>
    </xsl:template>

    <xsl:template match="products"/>

</xsl:stylesheet>
3
On

Have modified the code for not to use for loop

Please use below code ,it works as expected

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/" name ="ParentCopy">
<xsl:param name="LoopCountA">1</xsl:param>
<xsl:variable name = "Count" >
        <xsl:value-of select = "count(r/products)"/>
    </xsl:variable>
    <xsl:choose>
    <xsl:when test ="($LoopCountA  &lt;= $Count) ">
<xsl:choose>
            <xsl:when test ="r/products[$LoopCountA][@start &lt; 2 and @stop &gt; 2]">
                <test>
    <xsl:value-of select = "r/products[$LoopCountA]/p[1]/name"/>
                </test> 
<test>
                    <xsl:value-of select = "r/products[$LoopCountA]/p[2]/name"/>
</test>
            </xsl:when>
</xsl:choose>
    <xsl:call-template name="ParentCopy">
            <xsl:with-param name="LoopCountA">
<xsl:value-of select="$LoopCountA + 1"/>
            </xsl:with-param>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

Let me know for any other queries

2
On

You have to process all p elements, see the xsl:for-each here:

<xsl:template name="test">
    <test>
        <xsl:for-each select="*">
            <xsl:value-of select="name"/>
        </xsl:for-each>
    </test>
</xsl:template>
0
On

Q : Why am I not getting all the <p> with parent of specified attributes, only the first one? Mexico and HMargarethe was expected also.

Notice that template test only called once for each products element :

<xsl:template match="products[@start &lt; 2 and @stop &gt; 2]" >
    <xsl:call-template name="test"/>
</xsl:template>

Each time the template called, you pull multiple name elements to be processed by <xsl:value-of>. And according to MSDN regarding select attribute of <xsl:value-of> :

Required. The Expressions (XML) to be evaluated against the current context. The results are converted to a string, as by a call to the string() function. A node-set is converted to a string by inserting the string value of the first node in the set.

The specification also tells the same story, see W3C xslt value-of and xpath function string()