Mr. Boy
Mr. Boy

Reputation: 63816

Transform XML "yes/no" string to boolean

A legacy XML feed we want to consume (it's coupled to a specific database and has no XSD) emits "Y" or "N" as truth values. We are creating an XSD and auto-generating C# classes from this, with some transforms to make things neater.

So if I have fields like <IsFed>Y</IsFed> on an object, how can I transform these using XSLT so something that would validate against xsd:boolean?

I'm interested in two approaches:

  1. Explicitly listing each field to be transformed
  2. Automatically detecting every such yes/no field (I realise this could have errors)

Sample XML might look like this:

<Animal type="hamster">
 <IsFed>Y</IsFed>
 <Name>Gerald</Name>
</Animal>
<Animal type="cow">
 <IsFed>N</IsFed>
 <Name>acv4445-7</Name>
</Animal>

And it should come out like:

<Animal type="hamster">
 <IsFed>true</IsFed>
 <Name>Gerald</Name>
</Animal>
<Animal type="cow">
 <IsFed>false</IsFed>
 <Name>acv4445-7</Name>
</Animal>

Upvotes: 2

Views: 2709

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 117165

  1. Explicitly listing each field to be transformed

XSLT 1.0

<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:strip-space elements="*"/>

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

<!-- list every boolean element here -->
<xsl:template match="IsFed | HasShelter | etc.  ">
    <xsl:copy>
        <xsl:value-of select=".='Y'"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

  1. Automatically detecting every such yes/no field (I realise this could have errors)

XSLT 1.0

<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:strip-space elements="*"/>

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

<xsl:template match="text()[.='Y']">true</xsl:template>
<xsl:template match="text()[.='N']">false</xsl:template>

</xsl:stylesheet>

Upvotes: 3

Jim Garrison
Jim Garrison

Reputation: 86774

Interesting little problem. Here's one possible solution for XSLT 2 (XSLT 1 solution down below)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'Is') and matches(text(),'[YN]')]/text()">
        <xsl:value-of select="if (.='Y') then 'true' else 'false'"></xsl:value-of>
    </xsl:template>
</xsl:stylesheet>

This is an identity transform plus a template matching the text of any element whose name starts with Is and whose value is Y or N, which it replaces with true or false. It does not affect an element whose name starts with Is whose value is not Y or N.

Here's the same thing for XSLT 1.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[substring(name(),1,2) = 'Is' and (text() = 'Y' or text() = 'N')]/text()">
        <xsl:choose> 
            <xsl:when test=". = 'Y'">true</xsl:when>
            <xsl:otherwise>false</xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 3

Related Questions