Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select distinct values from XML document using XPATH?

Tags:

xml

xslt

xpath

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/*)

like image 699
Alex Avatar asked May 20 '10 07:05

Alex


People also ask

How can I get distinct values in XPath?

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.

What is XPath query in XML?

The XML Path Language (XPath) is used to uniquely identify or address parts of an XML document. An XPath expression can be used to search through an XML document, and extract information from any part of the document, such as an element or attribute (referred to as a node in XML) in it.

What is unique XPath?

Unlike ID attributes, every element in a web page has a unique XPath. An XPath (XML Path Language) is a query language for selecting nodes from XML like documents, such as HTML in our case.


1 Answers

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>
like image 52
Dimitre Novatchev Avatar answered Oct 05 '22 23:10

Dimitre Novatchev