How to count elements with the same attribute value

I am sure it is simple, but I just do not see a tree for trees.

I have an XML that looks 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 much / m * / @ a is 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 incorrect:

 <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 have a suggestion what am I doing wrong?

+5
source share
3 answers

Replace XPath with the correct one:

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

I used substring to match the first character of the attribute instead of contains , which matches any character in the string.

+6
source

I think this is what you need.

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

Using this in 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> 

displays the desired result

 <root> <count>0</count> <count>1</count> <count>1</count> </root> 
+1
source

Use (with the current node profil element):

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

And the full 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 to 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> 

creates the desired, correct result :

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

All Articles