[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Getting the XPath of a node

Subject: Re: Getting the XPath of a node
From: Peter Davis <pdavis152@xxxxxxxxx>
Date: Wed, 4 Sep 2002 02:44:03 -0700
getting xpath from node
On Wednesday 04 September 2002 02:03, Dennis wrote:
> Say if I have following XML:
> <Person id="12345">
>   <Name>Dennis</Name>
>   <Company>Netscape</Company>
>   <Address>Mountain View</Address>
>   <Email>dennis@xxxxxxxxxxxx</Email>
> </Person>
>
> ----The XSL to print XPath---
> <xsl:template match="Company">
> //Print the XPath of Company as /Person/Company
> </xsl:template>
> More templates corresponding to each element.

If you use Saxon exclusively, first check out 
<http://saxon.sourceforge.net/saxon6.5.2/extensions.html#path>.

If that doesn't help, then I guess the question I need to ask is, do you want 
to output an XPath that identifies a specific <Company> element (as in 
"/Person[@id='12345']/Company" or "/Person[1]/Company[1]"), or any <Company> 
in the whole document ("/Person/Company", as you indicate).

If your answer is the former, then there are several ways you can do it.  One 
would be to figure out a set of parameters that fully specify the location of 
the node in the document, which are specific to that particular type of node.  
For example, to identify a <Company>, you need to know Person/@id.  If you 
want to do this for every type of element, then you can just write a bunch of 
custom templates:

<xsl:template match="Company">
  Person[@id='<xsl:value-of select="parent::Person/@id"/>']/Company
</xsl:template>

and so on.

If you want a more generic solution, then you can go the route that 
saxon:path() uses, which is to simply use the position of the element and its 
parents within the document, which gives you something like 
"/Person[1]/Company[1]".  To do this, the basic idea would be to select each 
of the ancestors of the node in reverse document order.  You could probably 
mangle xsl:for-each and xsl:sort to do this, but I prefer a recursive 
template (untested):

<template match="*" name="output-xpath">
  <param name="node" select="."/>
  <text>/</text>
  <value-of select="name($node)"/>
  <text>[</text>
  <value-of select="count($node/../*[local-name() = local-name($node)
                          and namespace-uri() = namespace-uri($node)])"/>
  <text>]</text>
  <if test="..">
    <call-template name="output-xpath">
      <with-param name="node" select="$node/.."/>
    </call-template>
  </if>
</template>

[The above only works for elements (assuming it works at all, I haven't 
tested), so if you want to adapt it to text or other types of nodes, then all 
you should need is a big <xsl:choose> to special case outputting of "text()", 
processing-instruction()", etc.]

If you just want the XPath to identify *any* <Company> in the whole document, 
then you should be able to use the same template as above but just without 
the predicate stuff (the '[' + count() + ']').

HTH

-- 
Peter Davis

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.