5

I'm sure this is an easy one but i'm just not to see the wood for the trees.

I'm having an XML that look like this:

   <root>
    <profil>
        <e1 a="2">1</e1>
        <m1 a="3">1</m1>
        <e2 a="4">1</e2>
        <m2 a="5">1</m2>
    </profil>
    <profil>
        <e1 a="5">1</e1>
        <m1 a="3">1</m1>
        <e2 a="4">1</e2>
        <m2 a="4">1</m2>
    </profil>
    <profil>
        <e1 a="7">1</e1>
        <m1 a="7">1</m1>
        <e2 a="4">1</e2>
        <m2 a="2">1</m2>
    </profil>
</root>

Now I want to know how many /m*/@a are equal to e*/@a per /profil. So I came up with the following XSLT:

<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="*">
        <xsl:element name="root">
            <xsl:for-each select="/root/profil">
                <xsl:element name="count">
                    <xsl:value-of select="count(*[contains(name(), 'm') and ./@a = //*[contains(name(),'e')]/@a])"/>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

But the result is wrong:

<root>
    <count>1</count>
    <count>1</count>
    <count>2</count>
</root>

It should be

<root>
    <count>0</count>
    <count>1</count>
    <count>1</count>
</root>

Does anyone has a suggestion what I'm doing wrong?

2
  • Good question, +1. See my answer for a complete, short and easy solution that uses the most appropriate in your case standard XPath function: starts-with(). :) Commented Jun 18, 2011 at 20:42
  • I've change the title to match better the content of your question. be free to revert to the original if you care about it. Commented Jun 20, 2011 at 21:26

3 Answers 3

6

Replace the XPath with the correct one, which is:

<xsl:value-of select="count(*[substring(name(),1,1)='m' 
      and ./@a = ../*[substring(name(),1,1)='e']/@a])"/>

I've used substring to match the first attribute character in place of contains which matches any character in a string.

Sign up to request clarification or add additional context in comments.

2 Comments

Nice answer - I think substring is probably more efficient than contains and you beat me to it :) +1
Perfect and even better. Thanks
1

I think this is what you need

count(*[contains(name(), 'm') and (@a = parent::*/*[contains(name(),'e')]/@a)])

Using this in your XSLT

<?xml version="1.0" encoding="UTF-8"?>
<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="*">
        <xsl:element name="root">
            <xsl:for-each select="/root/profil">
                <xsl:element name="count">
                    <xsl:value-of select="count(*[contains(name(), 'm') and (@a = parent::*/*[contains(name(),'e')]/@a)])"/>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

produces the desired output

<root>
  <count>0</count>
  <count>1</count>
  <count>1</count>
</root>

1 Comment

Thanks to you also! Works also perfekt as I sad I was just to long looking at that XSLT.
0

Use (with current node any profil element):

count(*[starts-with(name(),'m')
      and
        @a = ../*[starts-with(name(),'e')]/@a
       ]
      )

And the complete XSLT code:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="profil">
  <count>
   <xsl:value-of select=
   "count(*[starts-with(name(),'m')
          and
            @a = ../*[starts-with(name(),'e')]/@a
           ]
         )
   "/>
  </count>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <profil>
        <e1 a="2">1</e1>
        <m1 a="3">1</m1>
        <e2 a="4">1</e2>
        <m2 a="5">1</m2>
    </profil>
    <profil>
        <e1 a="5">1</e1>
        <m1 a="3">1</m1>
        <e2 a="4">1</e2>
        <m2 a="4">1</m2>
    </profil>
    <profil>
        <e1 a="7">1</e1>
        <m1 a="7">1</m1>
        <e2 a="4">1</e2>
        <m2 a="2">1</m2>
    </profil>
</root>

produces the wanted, correct result:

<count>0</count>
<count>1</count>
<count>1</count>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.