[xsl] XSL to generate XPaths

Subject: [xsl] XSL to generate XPaths
From: Michael Strasser <M.Strasser@xxxxxxxxxxxxx>
Date: Wed, 16 May 2001 09:26:38 +1000
This XSL document generates a text list of all XPaths of element and
attribute nodes in an XML document. XPaths are unique and only include
subscripts when necessary.

I created this over the past couple of weeks to use in a project I'm
working on. Here it is for anybody else who may find it useful. You can, of
course, change the delimiter from newline (&#10;) to anything else (like a
comma).

All comments welcome. Sorry about e-mail line-wrapping. Have fun!

-- 
Michael Strasser
Brisbane, Australia

--- snip ---
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <!--
    all-xpaths.xsl
    
    Transform an XML document to a text listing of all its element and
    attribute nodes as unique XPath specifications.
    
    Output is a newline-separated list.
    
    Michael Strasser, May 2001
    M.Strasser@xxxxxxx
  -->
  <!-- Output is plain text. -->
  <xsl:output method="text" media-type="text/plain"/>
  <!-- Root element match: apply to all child nodes. -->
  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  <!-- Element and attribute match: this is where the work is done. -->
  <xsl:template match="*|@*">
    <xsl:param name="path" select="''"/>
    <!--
      Create this node's XPath specifier by appending information to
      the path param passed to this template. A variable is created
      because its value is used twice.

      This code is a bit complicated to make the output a bit pretty:
      if a node has no siblings with the same name there is no need
      to include a subscript to make the XPath unique.
    -->
    <xsl:variable name="this-path">
      <xsl:value-of select="concat($path, '/')"/>
      <!-- If this is an attribute node, output '@' before its name. -->
      <xsl:if test="../@*[name() = name(current())]">
        <xsl:text>@</xsl:text>
      </xsl:if>
      <xsl:value-of select="name()"/>
      <!--
        If the parent has more than one node with the same name as
        the current one...
      -->
      <xsl:if test="count(../*[name() = name(current())]) &gt; 1">
        <!--
          Include the current node's subscript in the list of those
          with the same name.
        -->
        <xsl:value-of select="concat('[', count(preceding-sibling::*[name()
= name(current())]) + 1, ']')"/>
      </xsl:if>
    </xsl:variable>
    <!-- If this is not the first node in the document, output a newline
first. -->
    <xsl:if test="preceding::*|ancestor::*">
      <xsl:text>&#10;</xsl:text>
    </xsl:if>
    <!-- Send this node's path to the output stream now. -->
    <xsl:value-of select="$this-path"/>
    <!-- Apply to this node's children, passing its path as a starting
point. -->
    <xsl:apply-templates select="*|@*">
      <xsl:with-param name="path" select="$this-path"/>
    </xsl:apply-templates>
  </xsl:template>
  <!-- Ignore comment, text and PI nodes. -->
  <xsl:template match="comment()|text()|processing-instruction()"/>
</xsl:stylesheet>

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


Current Thread