5

I need to select a node if a condition is matched. I'm totally new in XSLT and tried to search for an answer but can't find a solution that works for me. I want to select an adres when there is an AddressType = 'Personal', if there's not an AddressType = Personal, check for 'Second', if not check for 'Office'. A normal If Else. I tried with and and this could work if only one type of AddressType would exist in the XML file. In my case there can be up too 11 addressTypes (in random order) in the XML file and I can only pass one through.

This looks like my source XML:

<Person>
<address>
<street></street
<number></number>
<City></City>
<AddressType>Personal</AddressType>
</address>
<address>
<street></street
<number></number>
<City></City>
<AddressType>Second</AddressType>
</address>
<address>
<street></street
<number></number>
<City></City>
<AddressType>Office</AddressType>
</address>
</Person>

Any advice? thank you

2
  • It's a little unclear what your rules are. Do you want to filter address for only those containing either Personal or Second or Office or do you rather need to customize your output according to the AddressType? Commented Apr 2, 2012 at 7:01
  • user1292411: You may be interested to find a much shorter and concise solution than the currently accepted. It is not necessary to use xsl:choose at all. The size of the accepted solution will grow proportionally with the number of options. There is a simpler and shorter solution that is fixed-size, regardless of the number of options. :) Commented Apr 2, 2012 at 12:59

4 Answers 4

9

You need to use <xsl:choose>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <xsl:choose>
            <xsl:when test="/Person/address/AddressType='Personal'">
                <!-- DO Stuff -->
            </xsl:when>
            <xsl:when test="/Person/address/AddressType='Second'">
                <!-- DO Stuff -->
            </xsl:when>
            <xsl:when test="/Person/address/AddressType='Office'">
                <!-- DO Stuff -->
            </xsl:when>
            <xsl:otherwise>
                <!-- Do your else stuff -->
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this and it seems to work. So the first 'when' has priority over the next and etc...Is this correct?
It is not necessary to use xsl:choose at all. This solution will grow proportionally with the number of options. There is a simpler and shorter solution that is fixed-size, regardless of the number of options.
3

You could use xsl:choose:

<xsl:choose>
    <xsl:when test"some condition">
        Statement...
    </xsl:when>
    <xsl:when test="some other condition">
        Statement...
    </xsl:when>
    <xsl:otherwise>
        Default...
    </xsl:otherwise>
</xsl:choose>

Comments

3

Another option, rather than using nesting everything in an xsl:choose, is to use template matching to get the case you want. Try this XSLT as an example

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

   <xsl:template match="Person[address/AddressType='Personal']" priority="3">
      Match Personal
   </xsl:template>

   <xsl:template match="Person[address/AddressType='Second']" priority="2">
      Match Second
   </xsl:template>

   <xsl:template match="Person[address/AddressType='Office']" priority="1">
      Match Office
   </xsl:template>

   <xsl:template match="Person">
      Match None
   </xsl:template>
</xsl:stylesheet>

Do note that templates with more specific patterns take precedence over ones with no pattern (which is why the last "Person" template won't be called unless any of the other templates don't match. For the other templates, the higher the priority attribute will take precedence if all three templates match. Thus "Personal" will always be chosen even when the other attributes are present.

(If none of the templates had a priority attribute, the last matching one would be chosen in this case).

1 Comment

thx for the example. I tried to implement this in my biztalk project but I keep getting errors, probably due to the complexity of the actual schema in Biztalk. I used a choose with when, but will try this solution later on.
1

Here is a short and simple way to do this in XSLT 1.0:

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

 <xsl:param name="pAddressTypes" select="'PersonalSecondOffice'"/>
 <xsl:template match="/*">
     <xsl:apply-templates select="address">
       <xsl:sort select=
         "string-length(substring-before($pAddressTypes, AddressType))"/>
     </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="address">
  <xsl:if test="position() = 1">
    <xsl:copy-of select="."/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the following XML document (the originally provided one with addresses randomly reshuffled):

<Person>
    <address>
        <street></street>
        <number></number>
        <City></City>
        <AddressType>Office</AddressType>
    </address>
    <address>
        <street></street>
        <number></number>
        <City></City>
        <AddressType>Personal</AddressType>
    </address>
    <address>
        <street></street>
        <number></number>
        <City></City>
        <AddressType>Second</AddressType>
    </address>
</Person>

the wanted, correct address element is selected and processed (in this case just output):

<address>
   <street/>
   <number/>
   <City/>
   <AddressType>Personal</AddressType>
</address>

3 Comments

Am I missing something here or should the pAddressTypes parameter be set to something like "Office, Second, Personal" i.e. increasing order of priority so that the sort on string-length gets the highest priority address? Nice trick - I had some sort of map in mind but your approach is simpler.
@Kevan: The sort is ascending -- this means that in "PersonalSecondOffice" "Personal" would be sorted 1st -- and thus it has the highest priority.
Duh! - I didn't spot the single quotes around PersonalSecondOffice

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.