Why is this comparison only works for integer type id?

I am wondering does the statement [@id=current()/@id] can only work if the id is integer for example:

<elem id="1">
<elem id="1">

But if I have:

<elem id="AAA">
<elem id="AAA">

It does not work.

My XSL where it fails:

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                xmlns:tn="http://"
                exclude-result-prefixes="xsl xs fn tn">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*" />

    <xsl:template match="@*|node()">
        <xsl:copy> 
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy> 
    </xsl:template>

 <xsl:template match="genre/*">
        <xsl:copy>
            <xsl:apply-templates select="@*" />

            <xsl:apply-templates select="
     book[@id=current()/@id][@action='extend']                             
         [not( preceding-sibling::book[@id=current()/@id][@action='borrow'])]" />

            <xsl:for-each-group
                select="book[@id=current()/@id][@action='borrow'] 
             |   
            book[@id=current()/@id][@action='extend']
                [preceding-sibling::book[@id=current()/@id][@action='borrow']]"
                group-starting-with="book[@action='borrow']">
                <xsl:for-each select="current-group()[1]">
                    <xsl:copy>   
                        <xsl:apply-templates select="@*" />
                        <xsl:call-template name="merge-books-deeply">    
                            <xsl:with-param name="books" select="current-group()" />
                            <xsl:with-param name="name-path" select="()" />
                        </xsl:call-template>
                    </xsl:copy>
                </xsl:for-each>     
            </xsl:for-each-group>

            <xsl:apply-templates select="                             
     node()[ not( self::book[@id=current()/@id][@action=('borrow','extend')])]" />

        </xsl:copy>
    </xsl:template>

    <xsl:function name="tn:children-on-path" as="element()*">
        <xsl:param name="base" as="element()*" />   
        <xsl:param name="path" as="xs:string*" />    
        <xsl:choose>
            <xsl:when test="fn:empty($base)">
                <xsl:sequence select="()" />
            </xsl:when>
            <xsl:when test="fn:empty($path)">
                <xsl:copy-of select="$base/*" />     
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="tn:children-on-path(
     $base/*[name()=$path[1]],         
     $path[position() ne 1])" /> 
            </xsl:otherwise>  
        </xsl:choose>
    </xsl:function>

    <xsl:template name="merge-books-deeply">
        <xsl:param name="books" as="element()*" />
        <xsl:param name="name-path" as="xs:string*" />
        <xsl:for-each-group
            select="tn:children-on-path($books,$name-path)"
            group-by="name()">                                             
            <xsl:for-each select="current-group()[last()]" >  
                <xsl:copy>
                    <xsl:apply-templates select="@*" />
                    <xsl:call-template name="merge-books-deeply"> 
                        <xsl:with-param name="books" select="$books" />
                        <xsl:with-param name="name-path" select="$name-path,name()" />
                    </xsl:call-template>
                    <xsl:apply-templates select="text()" />        
                </xsl:copy>
            </xsl:for-each>
        </xsl:for-each-group>
    </xsl:template>

</xsl:stylesheet>

input sample where it works:

<root> 
    <library id="L1">
        <genre id="a">
            <shelf1 id="1">                
                <book id="1" action="borrow">
                    <attributes>
                        <user>John</user>                    
                    </attributes>
                    <other1>y</other1>
                </book>  
                <book id="1" action="extend">
                    <attributes>
                        <user>Woo</user>           
                        <length>3</length>
                    </attributes>
                    <other2>y</other2>
                </book>  
                <book id="1" action="extend">
                    <attributes>
                        <length>2</length>
                        <condition>ok</condition>
                    </attributes>
                    <other3>y</other3>
                </book>
                <book id="2" action="extend">
                    <attributes>
                        <length>99</length>
                        <condition>not-ok</condition>
                    </attributes>
                    <other>y</other>
                </book>
            </shelf1>
            <shelf1 id="b">...
            </shelf1>

        </genre>
        <genre id="b">...
        </genre>
    </library>
</root>

If I change the id of the book to 1a:

<root> 
    <library id="L1">
        <genre id="a">
            <shelf1 id="1">                
                <book id="1a" action="borrow">
                    <attributes>
                        <user>John</user>                    
                    </attributes>
                    <other1>y</other1>
                </book>  
                <book id="1a" action="extend">
                    <attributes>
                        <user>Woo</user>           
                        <length>3</length>
                    </attributes>
                    <other2>y</other2>
                </book>  
                <book id="1a" action="extend">
                    <attributes>
                        <length>2</length>
                        <condition>ok</condition>
                    </attributes>
                    <other3>y</other3>
                </book>
                <book id="2" action="extend">
                    <attributes>
                        <length>99</length>
                        <condition>not-ok</condition>
                    </attributes>
                    <other>y</other>
                </book>
            </shelf1>
            <shelf1 id="b">...
            </shelf1>

        </genre>
        <genre id="b">...
        </genre>
    </library>
</root>

My Error Output: (it doesn't merge at all)

<root> 
    <library id="L1">
        <genre id="a">
            <shelf1 id="1">                
                <book id="a1" action="borrow">
                    <attributes>
                        <user>John</user>                    
                    </attributes>
                    <other1>y</other1>
                </book>  
                <book id="a1" action="extend">
                    <attributes>
                        <user>Woo</user>           
                        <length>3</length>
                    </attributes>
                    <other2>y</other2>
                </book>  
                <book id="a1" action="extend">
                    <attributes>
                        <length>2</length>
                        <condition>ok</condition>
                    </attributes>
                    <other3>y</other3>
                </book>
                <book id="2" action="extend">
                    <attributes>
                        <length>99</length>
                        <condition>not-ok</condition>
                    </attributes>
                    <other>y</other>
                </book>
            </shelf1>
            <shelf1 id="b">...
            </shelf1>

        </genre>
        <genre id="b">...
        </genre>
    </library>
</root>

Expected Output:

<root> 
    <library id="L1">
        <genre id="a">
            <shelf1 id="1">                
                <book id="1a" action="borrow">
                    <attributes>
                        <user>Woo</user>           
                        <length>2</length>
                        <condition>ok</condition>                  
                    </attributes>
                    <other1>y</other1>
                </book>  
                <book id="2" action="extend">
                    <attributes>
                        <length>99</length>
                        <condition>not-ok</condition>
                    </attributes>
                    <other>y</other>
                </book>
            </shelf1>
            <shelf1 id="b">...
            </shelf1>

        </genre>
        <genre id="b">...
        </genre>
    </library>
</root>

For every node with action=borrow followed by one or more node with action=extend

  • Merge it together to the node with action=borrow.

  • Merge the attributes children together such that it will have all the unique attributes from the siblings with the latest value.

  • leave other children unchanged

THe merging only happen with node that has the same id.

Thanks. Regards, John

Answers


Maybe you are having a context issue instead. The comparisons should work. Here's an example...

XML Input

<tests>
    <test>
        <elem id="1"/>
        <elem id="1"/>
    </test>
    <test>
        <elem id="AAA"/>
        <elem id="AAA"/>
    </test>
    <test>
        <elem id="BBB"/>
        <elem id="CCC"/>
    </test>
    <test>
        <elem id="2"/>
        <elem id="3"/>
    </test>
</tests>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:template>

    <xsl:template match="elem[1]">
        <xsl:text>Comparing id </xsl:text>
        <xsl:value-of select="concat('&quot;',@id,'&quot;&#xA;')"/>
        <xsl:value-of select="concat('&#x9;',
            boolean(following-sibling::*[@id=current()/@id]),
            '&#xA;')"/>
    </xsl:template>

</xsl:stylesheet>

Output

Comparing id "1"
    true
Comparing id "AAA"
    true
Comparing id "BBB"
    false
Comparing id "2"
    false

Edit

After looking at your updated examples it definitely looks like a context issue. Take a look at the start of the template:

<xsl:template match="genre/*">
        <xsl:copy>
            <xsl:apply-templates select="@*" />

            <xsl:apply-templates select="
     book[@id=current()/@id][@action='extend']                             
         [not( preceding-sibling::book[@id=current()/@id][@action='borrow'])]" />

Notice that the match is genre/*; that's the current context. With your input that's going to be shelf1. Then the select of the second xsl:apply-templates starts with book[@id=current()/@id]. To me this looks like it is selecting book if it has an id attribute that equals the id attribute of shelf1. To test this you could also change the id of shelf1 to 1a and it would probably work. (That's the reason the first example works; the id of shelf1 and book match.

I can't test this theory right now, but I think that is the reason it appears that the comparison isn't working. (It is working, you're just not comparing what you think you're comparing.)


String comparison does not require explicit string conversion. In other words, it should work as you've written it. Here's an example:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="elem">
    <xsl:if test="count(../elem[@id=current()/@id]) &gt; 1">
      <xsl:value-of select="@id"/>: has dup
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

When applied to this input:

<r>
    <elem id="AAA">one</elem>
    <elem id="AAA">two</elem>
    <elem id="ZZZ">three</elem>
</r>

The following output is produced:

AAA: has dup
AAA: has dup

From the XPath spec:

Otherwise, if at least one object to be compared is a number, then each object to be compared is converted to a number as if by applying the number function. Otherwise, both objects to be compared are converted to strings as if by applying the string function.


Need Your Help

Custom intellisense for server controls?

controls intellisense

Can you program/configure Visual Studio to produce custom intellisense for your own server controls.