Madeyedexter
Madeyedexter

Reputation: 1193

Select Nodes using key() based on Condition

I have a situation where I need to select the nodes based on certain conditions and then apply Muenchian grouping to get unique values. I am on XSLT 1.0

<Parent>
    <Child>
        <A></A>
        <B></B>
        <C></C>
    </Child>
    <Child>
        <A></A>
        <B></B>
        <C></C>
    </Child>
    <Child>
        <A></A>
        <B></B>
        <C></C>
    </Child>
</Parent>

The Condition that I have for use attribute is: If A is not blank, use A. Otherwise use B.

I had thought of a solution that would work for Fixed Width values for Node A and B. So If A and B are of length n, I can do something like this:

<xsl:key name="groupKey" match="/Parent/Child" use="substring(concat(A,B),1,n)">

So, If A is not present, I can use B. But I haven't been able to figure out how to use string-length() to derive the expression for variable lengths for A and B. Any Ideas?

Upvotes: 2

Views: 910

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243579

In case you want to use the concat() function:

use="concat(A, B[not(string(../A)])"

And if by blank you mean empty or whitespace-only, then use:

use="concat(A, B[not(normalize-space(../A)])"

Finally, if you really want to use both concat() and substring() (and this is really tricky, so I don't recommend it):

concat(substring(A, 1 div string(A) or 0),
       substring(B, 1 div not(string(A)))
      )

Here is a complete example for the last case, showing what the call to concat() returns:

XML document:

<t>
    <p>
        <A>xxx</A>
        <B>yyy</B>
    </p>
    <p>
        <A/>
        <B>zzz</B>
    </p>
</t>

Transformation (just outputs the results of the evaluated expressions):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="p">
    <xsl:value-of select=
    "concat(substring(A, 1 div string(A) or 0),
            substring(B, 1 div not(string(A)))
            )"/>
  </xsl:template>
</xsl:stylesheet>

Wanted (correct) result produced:

xxx
zzz

Upvotes: 2

Daniel Haley
Daniel Haley

Reputation: 52888

Here's another option that uses a different use.

Example...

XML Input

<Parent>
    <Child>
        <A>both</A>
        <B>both</B>
        <C>c</C>
    </Child>
    <Child>
        <A>a</A>
        <B></B>
        <C>c</C>
    </Child>
    <Child>
        <A></A>
        <B>b</B>
        <C>c</C>
    </Child>
</Parent>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="child" match="Child" use="(A[string()]|B[string()])[1]"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:for-each select="Child[count(.|key('child',(A[string()]|B[string()])[1])[1])=1]">
        <group key="{(A[string()]|B[string()])[1]}">
          <xsl:apply-templates select="key('child',(A[string()]|B[string()])[1])"/>
        </group>
      </xsl:for-each>        
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XML Output

<Parent>
   <group key="both">
      <Child>
         <A>both</A>
         <B>both</B>
         <C>c</C>
      </Child>
   </group>
   <group key="a">
      <Child>
         <A>a</A>
         <B/>
         <C>c</C>
      </Child>
   </group>
   <group key="b">
      <Child>
         <A/>
         <B>b</B>
         <C>c</C>
      </Child>
   </group>
</Parent>

Upvotes: 1

michael.hor257k
michael.hor257k

Reputation: 117140

How about:

use="A | B[not(string(../A))]"

Upvotes: 1

Related Questions