Distinct values across XSLT nodesets

409 views Asked by At

Once again about the distinct nodes (basing on attribute values) in node set. Imagine u have the following structure:

<struct>
  <a>
    <x id="1">a_1</x>
    <x id="2">a_2</x>
    <x id="3">a_3</x>
  </a>
  <b inherits="a">
    <x id="2">b_2</x>
  </b>
</struct>

<struct/> may contain multiple elements like <b/> which inherit the same <a/>. At the same time multiple elements like <a/> are allowed. Order of <a/>s and <b/>s is arbitrary. Inheritance is single-level deep.

Question: How to create a single XPath that selects the following nodeset for a given <b/>:

<x id="1">a_1</x>
<x id="2">b_2</x>
<x id="3">a_3</x>

Please note the b_2 value on the second line.

Any solutions to this?

Update:

The resuting XPath should have the following form: b[(magic_xpath)[@id=2]='b_2'], where magic_xpath selects distinct <x/>s from <a/>s and <b/>s.

The real life <struct/> can look like this:

<struct>
  <a>
    <x id="1">a_1</x>
    <x id="2">a_2</x>
    <x id="3">a_3</x>
  </a>
  <b inherits="a">
    <x id="2">I don't match resulting XPath</x>
  </b>
  <b inherits="a">
    <x id="2">b_2</x>
  </b>
</struct>
1

There are 1 answers

4
Dimitre Novatchev On

Use:

  $vB/x
 |
  /*/*[name() = $vB/@inherits]
                /x[not(@id = $vB/x/@id)]

where $vB is defined to be the b element.

This selects the union of all b/x elements and all x children (with id attribute that isn't equal to any b/x/@id attribute) of struct/* elements (children of struct) whose name is the value of b/@inherits.

Or, as requested by the OP in a comment -- without variables:

  /*/b/x
 |
  /*/*[name() = /*/b/@inherits]
                /x[not(@id = /*/b/x/@id)]

Complete XSLT-based verification:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "/*/b/x
     |
      /*/*[name() = /*/b/@inherits]
                    /x[not(@id = /*/b/x/@id)]

   "/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided (corrected to be well-formed) XML document:

<struct>
    <a>
        <x id="1">a_1</x>
        <x id="2">a_2</x>
        <x id="3">a_3</x>
    </a>
    <b inherits="a">
        <x id="2">b_2</x>
    </b>
</struct>

the single XPath expression is evaluated and the selected nodes are output:

<x id="1">a_1</x>
<x id="3">a_3</x>
<x id="2">b_2</x>