Tony J
Tony J

Reputation: 107

XML Data Type Conversion

I have a need to convert all Float and Double data types as defined by a source schema to decimal data types.

We have an existing XSLT that takes an XML document from a third party and transforms it to a XML structure that we can then pass into our 4GL DB appliction. The nice thing about the native 4GL interface is that it can convert an inbound XML into a native Dataset structure. The short fall is that it maps Float and Double data types to character data types.

Is there a way within an XSLT to identify an element that is a Float/Double data type and convert it to a decimal. I was thinking this could be a pre main XSLT step and then pass the output onto the main XSLT.

Sample Schema In:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="ttPTManifest">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="JobCode"/>
                <xs:element ref="EstimateHours"/>
                <xs:element ref="ActualHours"/>
                <xs:element ref="Density"/>
                <xs:element ref="NettLitres"/>
                <xs:element ref="QuitOutLitres"/>
                <xs:element ref="QuitInLitres"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="QuitOutLitres" type="xs:double"/>
    <xs:element name="QuitInLitres" type="xs:float"/>
    <xs:element name="PTManifestSonicIn">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ttPTManifest"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="JobCode" type="xs:string"/>
    <xs:element name="NettLitres" type="xs:double"/>
    <xs:element name="EstimateHours" type="xs:float"/>
    <xs:element name="Density" type="xs:decimal"/>
    <xs:element name="ActualHours" type="xs:float"/>
</xs:schema>

Sample XML Data In:

<?xml version="1.0"?>
<PTManifestSonicIn xsi:noNamespaceSchemaLocation="SampleIn.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ttPTManifest>
        <JobCode>000123</JobCode>
        <EstimateHours>3.14159E3</EstimateHours>
        <ActualHours>3.14159E3</ActualHours>
        <Density>123.456</Density>
        <NettLitres>3.14159265358979E3</NettLitres>
        <QuitOutLitres>3.14159265358979E3</QuitOutLitres>
        <QuitInLitres>3.14159E3</QuitInLitres>
    </ttPTManifest>
</PTManifestSonicIn>

Rerquired Schema Out:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="ttPTManifest">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="JobCode"/>
                <xs:element ref="EstimateHours"/>
                <xs:element ref="ActualHours"/>
                <xs:element ref="Density"/>
                <xs:element ref="NettLitres"/>
                <xs:element ref="QuitOutLitres"/>
                <xs:element ref="QuitInLitres"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="QuitOutLitres" type="xs:decimal"/>
    <xs:element name="QuitInLitres" type="xs:decimal"/>
    <xs:element name="PTManifestSonicIn">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ttPTManifest"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="JobCode" type="xs:string"/>
    <xs:element name="NettLitres" type="xs:decimal"/>
    <xs:element name="EstimateHours" type="xs:decimal"/>
    <xs:element name="Density" type="xs:decimal"/>
    <xs:element name="ActualHours" type="xs:decimal"/>
</xs:schema>

Required XML Data Out:

<?xml version="1.0"?>
<PTManifestSonicIn xsi:noNamespaceSchemaLocation="SampleOut.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ttPTManifest>
        <JobCode>000123</JobCode>
        <EstimateHours>3141.59</EstimateHours>
        <ActualHours>3141.59</ActualHours>
        <Density>123.456</Density>
        <NettLitres>3141.59265358979</NettLitres>
        <QuitOutLitres>3141.59265358979</QuitOutLitres>
        <QuitInLitres>3141.59</QuitInLitres>
    </ttPTManifest>
</PTManifestSonicIn>

Upvotes: 2

Views: 7158

Answers (2)

Sbof
Sbof

Reputation: 51

If you can use XSL v2, use Mads' answer. If you're stuck with v1, This can do the XSD transformation:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="xs:element[@type='xs:double' or @type='xs:float']">
        <xsl:copy>
            <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
            <xsl:attribute name="type">xs:decimal</xsl:attribute>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

And this could be used to transform the XML:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="1.0">
    <xsl:output indent="yes"/>

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

    <xsl:template 
          match="*
                  [local-name(.)=document(/*/@xsi:noNamespaceSchemaLocation)/*/xs:element[@type='xs:double' or @type='xs:float']/@name]
                  [number(.)=number(.)]">
        <xsl:copy>
            <xsl:value-of select="number(.)"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Mads Hansen
Mads Hansen

Reputation: 66781

In order to transform the XML data, you could use the following stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="2.0">
    <xsl:output indent="yes"/>

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

    <xsl:template 
           match="*
                  [local-name(.)=document(/*/@xsi:noNamespaceSchemaLocation)/*/xs:element[@type=('xs:double', 'xs:float')]/@name]
                  [. castable as xs:double or . castable as xs:float]">
        <xsl:copy>
            <xsl:value-of select="number(.)"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

You could transform the Schema with the following stylesheet:

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

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

    <xsl:template match="@type[.=('xs:double', 'xs:float')]">
        <xsl:attribute name="type" select="'xs:decimal'"/>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions