19

How can I select only distinct elements for the XML document using XPATH?I've tried to use the 'distinct-values' function but it didn't work for some reason..

The XML is similar to the following:

<catalog>

<product>
<size>12</size>
<price>1000</price>
<rating>1</rating>
</product>

<product>
<size>10</size>
<price>1000</price>
<rating>1</rating>
<year>2010</year>
</product>

</catalog>

So what I want to get is the list of distinct children of all the product elements.In the given example it would be - size,price,rating,year My xpath was something like : distinct-values(catalog/product/*)

4
  • distinct-values function used for only one value for and inline content or attribute content.What do you mean by distinct children exactly?Is it different element names like size,price,rating,not the values ? Commented May 20, 2010 at 7:45
  • Sorry..yeah,exactly.I want to get the list of all the distinct children element names Commented May 20, 2010 at 7:47
  • You may not be grab distinct names by XPath ! Commented May 20, 2010 at 7:58
  • 1
    Excellent Question (+1). See my answer for solutions in XPath 2.0, and XSLT 1.0. Commented May 20, 2010 at 13:24

3 Answers 3

23

In XPath 2.0:

distinct-values(/*/*/*/name(.))

In XPath 1.0 this cannot be produced with a single XPath expression.

Using XSLT 1.0:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/">
   <xsl:for-each select=
   "/*/*/*[not(../following::*/*
                       [not(name() = name(current()))]
               )
           ]">
     <xsl:value-of select="concat(name(), ' ')"/>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document, the wanted result is produced:

size price rating year

A more efficient XSLT 1.0 transformation, using keys:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kpchildByName"
  match="product/*" use="name()"/>

 <xsl:template match="/">
   <xsl:for-each select=
   "/*/*/*
         [generate-id()
         =
          generate-id(key('kpchildByName', name())[1])
          ]">
     <xsl:value-of select="concat(name(), ' ')"/>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

Comments

7

You need the distinct values of the element names - something like:

distinct-values($catalog/product/*/name(.))

Comments

4

distinct-values() is available in XPath 2.0. Are you using that?

If distinct-values() is not available, the standard way of getting distinct values is to use not(@result = preceding:: @result) to get unique @result. It will give you the first occurrence only.

2 Comments

I'm not sure which XPATH version I've got:) How can I find out about that?
That would only work if the list is sorted on your result

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.