20

I am using XSLT 1.0. My input information may contain these values

<!--case 1-->
<attribute>123-00</attribute>

<!--case 2-->
<attribute>Abc-01</attribute>

<!--case 3-->
<attribute>--</attribute>

<!--case 4-->
<attribute>Z2-p01</attribute>

I want to find out those string that match the criteria:

if string has at least 1 alphabet AND has at least 1 number,
then 
do X processing
else
do Y processing

In example above, for case 1,2,4 I should be able to do X processing. For case 3, I should be able to do Y processing.

I aim to use a regular expression (in XSLT 1.0).

For all the cases, the attribute can take any value of any length.

I tried use of match, but the processor returned an error. I tried use of translate function, but not sure if used the right way.

I am thinking about.

if String matches [a-zA-Z0-9]* 
then do X processing
else
do y processing.

How do I implement that using XSLT 1.0 syntax?

Wayne
  • 59,728
  • 15
  • 131
  • 126
kapil.murarkar
  • 247
  • 1
  • 3
  • 6

3 Answers3

17

This solution really works in XSLT 1.0 (and is simpler, because it doesn't and needn't use the double-translate method.):

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:variable name="vUpper" select=
 "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

 <xsl:variable name="vLower" select=
 "'abcdefghijklmnopqrstuvwxyz'"/>

 <xsl:variable name="vAlpha" select="concat($vUpper, $vLower)"/>

 <xsl:variable name="vDigits" select=
 "'0123456789'"/>

 <xsl:template match="attribute">
  <xsl:choose>
   <xsl:when test=
    "string-length() != string-length(translate(.,$vAlpha,''))
    and
     string-length() != string-length(translate(.,$vDigits,''))">

    Processing X
   </xsl:when>
   <xsl:otherwise>
    Processing Y
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML fragment -- made a well-formed XML document:

<t>
    <!--case 1-->
    <attribute>123-00</attribute>
    <!--case 2-->
    <attribute>Abc-01</attribute>
    <!--case 3-->
    <attribute>--</attribute>
    <!--case 4-->
    <attribute>Z2-p01</attribute>
</t>

the wanted, correct result is produced:

Processing Y


Processing X

Processing Y


Processing X

Do Note: Any attempt to use with a true XSLT 1.0 processor code like this (borrowed from another answer to this question) will fail with error:

<xsl:template match=
"attribute[
           translate(.,
                     translate(.,
                               concat($upper, $lower),
                               ''),
                     '')
         and
           translate(., translate(., $digit, ''), '')]
 ">

because in XSLT 1.0 it is forbidden for a match pattern to contain a variable reference.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Right, of course, about the match pattern, but double-translate vs. string-length comparison is simply preference/opinion. – Wayne Jan 19 '12 at 15:12
  • @lwburk: So, you are saying that *double* translate isn't more complex than *single* translate? :) – Dimitre Novatchev Jan 19 '12 at 16:35
  • @Dimitre - Hehe, well `translate(.,translate(.,$vAlpha,''),'')` is 16 fewer characters than `string-length()!=string-length(translate(.,$vAlpha,''))` and while I'll agree that it looks a little odd, at first, I'm sure you'll agree that it's an easily recognized pattern once you've used it a few times. – Wayne Jan 19 '12 at 17:07
  • @Dimitre - Plus, the string-length solution has *three* function calls to double-translate's *two* :) – Wayne Jan 19 '12 at 17:09
3

If you found this question because you're looking for a way to use regular expressions in XSLT 1.0, and you're writing an application using Microsoft's XSLT processor, you can solve this problem by using an inline C# script.

I've written out an example and a few tips in this thread, where someone was seeking out similar functionality. It's super simple, though it may or may not be appropriate for your purposes.

Community
  • 1
  • 1
melanie johnson
  • 597
  • 3
  • 16
2

XSLT does not support regular expressions, but you can fake it.

The following stylesheet prints an X processing message for all attribute elements having a string value containing at least one number and at least one letter (and Y processing for those that do not):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
    <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
    <xsl:variable name="digit" select="'0123456789'"/>
    <xsl:template match="attribute">
        <xsl:choose>
            <xsl:when test="
                translate(., translate(., concat($upper, $lower), ''), '') and 
                translate(., translate(., $digit, ''), '')">
                <xsl:message>X processing</xsl:message>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message>Y processing</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Note: You said this:

In example above, for case 1,2,4 I should be able to do X processing. for case 3, I should be able to do Y processing.

But that conflicts with your requirement, because case 1 does not contain a letter. If, on the other hand, you really want to match the equivalent of [a-zA-Z0-9], then use this:

translate(., translate(., concat($upper, $lower, $digit), ''), '')

...which matches any attribute having at least one letter or number.

See the following question for more information on using translate in this way:

Community
  • 1
  • 1
Wayne
  • 59,728
  • 15
  • 131
  • 126
  • 1
    Unfortunately, this solution doesn't work with a real XSLT 1.0 processor. It would be good if you try an XSLT 1.0 solution with an XSLT 1.0 processor. Of course, this works with an XSLT 2.0 processor, but nobody would write such things in XSLT 2.0. – Dimitre Novatchev Jan 19 '12 at 03:59
  • 1
    @DimitreNovatchev is right, of course. In practice, this ends up working in a lot of XSLT 1.0 processors (which is the primary reason this error wasn't obvious to me when I tested my solution) but it is technically forbidden by the spec. In any event, the core of the solution -- the double-translate method -- is valid. – Wayne Jan 19 '12 at 15:14
  • Moved variable out of match pattern, so that no future visitors are mislead. Thanks to @Dimitre for pointing this out. I'm sure I'll forget again in about a week when yet another XSLT 1.0 processor lets me get away with it. – Wayne Jan 19 '12 at 15:21
  • @lwburk: I'a just curious: Which XSLT 1.0 processors allow this? – Dimitre Novatchev Jan 19 '12 at 16:36
  • @Dimitre - It works in RAD's default XSLT processor ("IBM Processor for XSLT 1.0" (Run as... -> XSL Transformation)) and Firefox 9.0.1's built-in XSLT processor, as examples. – Wayne Jan 19 '12 at 16:56
  • @Dimitre - Just tossed together a Xalan/JAXP example and it worked there, too. In all three of these cases, the use of any other XSLT/XPath 2.0 feature fails, but variables in match patterns are allowed. – Wayne Jan 19 '12 at 17:00
  • @lwburk: These are all non-compliant XSLT processors -- better not to use them. I am using these compliant XSLT 1.0 processors: Saxon 6.5.x, MSXML3, MSXML4, MSXML6, .NET XslCompiledTransform, .NET XslTransform, XML-SPY (Altova). – Dimitre Novatchev Jan 19 '12 at 18:07
  • 3
    Xalan has always allowed variables in match patterns. They were allowed in many working drafts of XSLT 1.0; the restriction was introduced at a late stage because of worries about circularity, but Xalan (or LotusXSL as it was then) never implemented the restriction. – Michael Kay Jan 21 '12 at 22:49