views:

37

answers:

2

I'm trying to create an address that validates against a schema set in stone, it requires that at least 2 of the 5 lines have been entered.

Only show address node if at least two of the five lines are available.

Is there a way I can check this using XSL

The input looks like this:

<Services Street="1 The Road " ExtraAddress="The Street" 
VillageTownName="" PostalTownName="" County="">

Required valid output

<Address>
<line>1 The Road</line>
<line>The Street</line
</Address>

Thanks,

Mark

+3  A: 

Count all of the attributes that have values, and use that count to drive the validation:

  <xsl:template match="Services">
    <xsl:variable name="line-count"
       select="count(
      ./@*[(name() = 'Street' 
      or name() = 'ExtraAddress'
      or name() = 'VillageTownName'
      or name() = 'PostalTownName'
      or name() = 'County')
      and string-length(.) &gt; 0]
     )"
  />
    <xsl:choose>
      <xsl:when test="$line-count &gt; 1">
        <xsl:text>Valid</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>Invalid</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

If those are the only attributes that are allowed to appear in the Services element, then you can remove the explicit name checks and use @* as a selector instead.

I've tested this using XSLT 1.0 and it works just fine.

You'll need to place an apply-template element in place of my text element in the valid case, to print the address element. That's the easy part.

Welbog
While I do appreciate you accepting my answer, Kitemark, I believe Tomalak's answer is superior to mine. I would advise following his solution rather than mine in this situation.
Welbog
+2  A: 
<!-- this outputs an <Address> element if appropriate -->
<xsl:template match="Services">
  <xsl:variable name="lines" select="@*[not(normalize-space() = '')]" />
  <xsl:if test="count($lines) &gt;= 2">
    <Address>
      <xsl:apply-templates select="$lines" />
    </Address>
  </xsl:if>
</xsl:template>

<!-- this outputs the individual address lines -->
<xsl:template match="Services/@*">
  <line>
    <xsl:value-of select="." />
  </line>
</xsl:template>

The expression @*[not(normalize-space() = '')] actually implies that every attribute of the <Service> element is relevant. If you want/need to make that more explicit, I suggest:

(@Street|@ExtraAddress|@VillageTownName|@PostalTownName|@County)[
  not(normalize-space() = '')
]
Tomalak
Oh you sneaky Tomalak. That seems like an awful kludge of a way to count things.
Welbog
Even sneakier. +1 for not using a kludge.
Welbog
Yeah, I knew instantly how stupid version #1 was, but did not manage to rewrite within that dreaded 5 min time frame. :-D Thanks for the up-vote. ;-)
Tomalak
I forgot you could do that with the `|` operator. If I could +1 you again for that I would. Curse you for having a strictly better answer than mine. *[shakes fist]*
Welbog
Hey, no threatening around here. ;-) Anyway, virtual up-votes are happily accepted. :-)
Tomalak