xslt: traverse chain of ancestors in reverse order

657 views Asked by At

Suppose I have the following xml-document:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="FirstLevel">
    <xs:unique name="uniqueL2inL1">
      <xs:selector xpath="SecondLevel" />
      <xs:field xpath="@Name" />
    </xs:unique>
  </xs:element>
  <xs:element name="SecondLevel">
    <xs:unique name="uniqueL3inL2">
      <xs:selector xpath="ThirdLevel" />
      <xs:field xpath="@Name" />
    </xs:unique>
  </xs:element>
  <xs:element name="ThirdLevel">
    <xs:unique name="uniqueL4inL3">
      <xs:selector xpath="FourthLevel" />
      <xs:field xpath="@Name" />
    </xs:unique>
  </xs:element>
  <xs:element name="FourthLevel"/>
</xs:schema>

What possibilities are there to get the "chain" of unique-definitions from the ThirdLevel element up to the document root in a correct ordering?

The resulting output I wanna have for ThirdLevel for example:

FirstLevel
SecondLevel

To get the node of FirstLevel (eg root-element) I need to query an xs:element which has an xs:unique, but whose name itself is nowhere else used in an xs:unique/xs:selector/@xpath. This specific query is:

<xsl:variable name="root-element" select="//xs:element[xs:unique and not(//xs:element/xs:unique/xs:selector/@xpath = @name)]/@name"/>

Now I need to get all nodes between, say, ThirdLevel and this $root-element.

I have a recursively called template with two parameters (can attach, but left aside for clarity) which outputs all the nodes.

Is it possible to query with one xpath?

Greetings

1

There are 1 answers

0
Martin Honnen On BEST ANSWER

I am not sure the whole approach makes sense for complex schemas and arbitrary XPath selectors but if the current approach gives you the output you want, only in the wrong order then you could simply store the output in a variable and reverse that variable contents when you output the values:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" >
  <xsl:output method="text" />
  <xsl:template match="xs:element">
    <xsl:result-document href="chain_{@name}">
      <xsl:variable name="element-name" select="@name"/>
      <xsl:variable name="parent-name" select="//xs:unique/xs:selector[@xpath=$element-name]/@xpath"/>
      <xsl:if test="$parent-name != ''">
        <xsl:variable name="list" as="xs:string*">
          <xsl:call-template name="mytemplate">
            <xsl:with-param name="unique-block" select="//xs:unique/xs:selector[@xpath=$element-name]/.."/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="reverse($list)" separator="&#10;"/>
      </xsl:if>
    </xsl:result-document>
  </xsl:template>
  <xsl:template name="mytemplate">
    <xsl:param name="unique-block"/>
    <xsl:variable name="parent-name" select="$unique-block/../@name"/>
    <xsl:sequence select="$parent-name"/>
    <xsl:variable name="unique-selector" select="//xs:unique/xs:selector[@xpath=$parent-name]/@xpath"/>
    <!-- evaluate and recurse, if needed: -->
    <xsl:if test="$unique-selector != ''">
      <xsl:call-template name="mytemplate">
        <xsl:with-param name="unique-block" select="//xs:unique/xs:selector[@xpath=$parent-name]/.." />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>