Brad
Brad

Reputation: 3

Converting XML single elements to XML attributes using XSLT and keeping as elements in output

We have a current system that outputs an XML file which is in the following format:

<ResultSet rowCount="2">
    <Row>
     <Entry>83708</Entry>
     <mark>L24653338N1</mark>
     <Processed>NO</Processed>
    </Row>
    <Row>
     <Entry>99999</Entry>
     <mark>L24653338N1</mark>
     <Processed>YES</Processed>
    </Row>
</ResultSet>

I need to transform into:

<ResultSet rowCount="2">
    <Row Processed="NO">
     <Entry>83708</Entry>
     <mark>L24653338N1</mark>
     <Processed>NO</Processed>
    </Row>
    <Row Processed="YES">
     <Entry>99999</Entry>
     <mark>L24653338N1</mark>
     <Processed>YES</Processed>
    </Row>
</ResultSet>

Does someone know how this can be done using .XSL?

Here is what I have done:

</xsl:template>
    <xsl:template name="transform">
        <Row>
        <xsl:if test="$linecount&gt;0">
            <xsl:for-each       select="/Msg/Body/Payload[./@Role='S']/Msg/Body/Payload[./@sql]/SqlResult/ResultSet/Row">
                <xsl:attribute name="pos"><xsl:value-of select="position()"/></xsl:attribute>
                <Entry>
                    <xsl:value-of select="./Entry"/>
                </Entry>
                <mark>
                    <xsl:value-of select="./mark"/>
                </mark>
                <Proccessed>
                    <xsl:value-of select="./Proccessed"/>
                </Proccessed>
            </xsl:for-each>
        </xsl:if>
    </Row>
</xsl:template>

Upvotes: 0

Views: 142

Answers (1)

Tim C
Tim C

Reputation: 70648

In tasks such as these, where you are only making changing to part of the XML, the usual approach is to start with the Identity Transform

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

On its own it will copy all the nodes and attributes as-is, so you only need to write templates for the things you wish to change. In your case you are adding a new Processed attribute onto the Row element. This means you only need a template that matches the Row element that then adds this attribute, like so

<xsl:template match="Row">
    <Row Processed="{Processed}">
        <xsl:apply-templates select="@*|node()"/>
    </Row>
</xsl:template>

Notice the use of Attribute Value Templates in the attribute you are creating. The curly braces {} indicate an expression to be evalualted, not output literally, and so the value of the attribute will actually be the value of the Processed element.

Try this XSLT

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

    <xsl:template match="Row">
        <Row Processed="{Processed}">
            <xsl:apply-templates select="@*|node()"/>
        </Row>
    </xsl:template>

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

Note that you call also create attributes with xsl:attribute. For example

<xsl:template match="Row">
    <Row>
        <xsl:attribute name="Processed">
            <xsl:value-of select="Processed" />
        </xsl:attribute>
        <xsl:apply-templates select="@*|node()"/>
    </Row>
</xsl:template>

But as you can see, this is more verbose, so Attribute Value Templates are preferred where possible.

Upvotes: 1

Related Questions