RJohnson
RJohnson

Reputation: 188

XSLT to determine which node to pick

I have an xml structure that looks like this:

<Person>
  <ID> 123 </ID>
  <Type> L </Type>
</Person>    
<Person>
  <ID> 456 </ID>
  <Type> N </Type>
</Person>    
<Person>
  <ID> 123 </ID>
  <Type> U </Type>
</Person>

Person is repeating, unlimited, but I only care about these 3 "types". I would like to somehow only select based on Type, but prioritize it, somehow.

I need to always choose the ID when type is L, regardless of what else is there. If L isn't there, choose N, and if N isn't there, U. Finally if U isn't there, don't output a node at all.

I've tried nested choose: (I apologize if this is syntactically incorrect, I'm manually typing to remove complexity.

<xsl:choose>
  <xsl:when test = "Person/Type='L'>
    <L-ID>
      <xsl:value-of "Person[Type = 'L']/ID" />
    </L-ID>
  </xsl:when>
  <xsl:otherwise>
      <xsl:when test = "Person/Type='N'>
        <N-ID>
          <xsl:value-of "Person[Type = 'N']/ID" />
        </N-ID>
      </xsl:when>
      <xsl:otherwise>
          <xsl:when test = "Person/Type='U'>
            <U-ID>
              <xsl:value-of "Person[Type = 'U']/ID" />
            </U-ID>
          </xsl:when>
      </xsl:otherwise>
  </xsl:otherwise>
</xsl:choose>

I've also thought of maybe using a combination of Ifs and Chooses but I can't quite wrap my head around the three conditions. I've tried mapping out truth tables, and just can't get it to do what I want.

If it helps, a table of what I want is this:

 U | L | N |   Output  | XML
---+---+---+-----------+
 1   1   1    L        | <L-ID> 123 </L-ID>
 1   1   0    L        | <L-ID> 123 </L-ID>
 1   0   1    N        | <N-ID> 456 </N-ID> 
 1   0   0    U        | <U-ID> 789 </U-ID> 
 0   1   1    L        | <L-ID> 123 </L-ID>
 0   1   0    L        | <L-ID> 123 </L-ID>
 0   0   1    N        | <N-ID> 456 </N-ID> 
 0   0   0    Nothing  |

Upvotes: 1

Views: 36

Answers (1)

Tim C
Tim C

Reputation: 70598

The syntax for your xsl:choose is this...

    <xsl:choose>
      <xsl:when test="Person/Type=' L '">
        <L-ID>
          <xsl:value-of select="Person[Type = ' L ']/ID" />
        </L-ID>
      </xsl:when>
      <xsl:when test="Person/Type=' N '">
        <N-ID>
          <xsl:value-of select="Person[Type = ' N ']/ID" />
        </N-ID>
      </xsl:when>
      <xsl:when test="Person/Type=' U '">
        <U-ID>
          <xsl:value-of select="Person[Type = ' U ']/ID" />
        </U-ID>
      </xsl:when>
    </xsl:choose>
</xsl:template>

Do note the use of spaces in the check, as you have spaces around the letters in the type node; <Type> L </Type>. An alternate way to write the xsl:when tests would be this

      <xsl:when test="Person[normalize-space(Type)= 'L']">
        <L-ID>
          <xsl:value-of select="Person[normalize-space(Type)= 'L']/ID" />
        </L-ID>
      </xsl:when>

Alternatively, you could take advantage of the fact the letters you want to select are in alphabetical order, so you can also do this..

<xsl:for-each select="Person[normalize-space(Type) = 'L' or normalize-space(Type) = 'N' or normalize-space(Type) = 'U']">
    <xsl:sort select="Type" />
    <xsl:if test="position() = 1">
        <xsl:element name="{normalize-space(Type)}-ID">
            <xsl:value-of select="ID" />
        </xsl:element>
    </xsl:if>
</xsl:for-each>

If you could use XSLT 2.0, you could simplify the xsl:for-each to this...

<xsl:for-each select="Person[normalize-space(Type) = ('L', 'N', 'U')]">

Upvotes: 1

Related Questions