This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: Getting the XPath of a node
- From: Peter Davis <pdavis152 at attbi dot com>
- To: xsl-list at lists dot mulberrytech dot com
- Date: Wed, 4 Sep 2002 02:44:03 -0700
- Subject: Re: [xsl] Getting the XPath of a node
- References: <20020904090309.83146.qmail@web20901.mail.yahoo.com>
- Reply-to: xsl-list at lists dot mulberrytech dot com
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@netscape.com</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