jFrenetic
jFrenetic

Reputation: 5542

XSLT - transform several sibling tags, but keep the rest intact

I'm trying to transform my input XML:

<?xml version="1.0" encoding="utf-8"?>
<request>
    <customer_data>
        <contact_info>
            <name>john doe</name>
            <dob_year>1984</dob_year>
            <dob_month>09</dob_month>
            <dob_date>14</dob_date>
            <gender>m</gender>
        </contact_info>
    </customer_data>
</request>

so that it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<request>
    <customer_data>
        <contact_info>
            <name>john doe</name>
            <dob>1984-09-14</dob>
            <gender>m</gender>
        </contact_info>
    </customer_data>
</request>

Here is the XSLT, that I'm using:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

    <xsl:template match="contact_info">
        <xsl:copy>
            <dob>
                <xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
            </dob>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

And here is the result that I get:

<?xml version="1.0" encoding="utf-8"?><request>
    <customer_data>
        <contact_info><dob>1984-09-14</dob></contact_info>
    </customer_data>
</request>

How do I transform the dob_* tags, keeping the rest of contact_info intact?

UPDATE

Based, on the answers below, my current solution is

<xsl:template match="contact_info">
    <xsl:copy>
        <xsl:apply-templates select="*[not(starts-with(name(), 'dob'))]"/>
        <dob>
            <xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
        </dob>
    </xsl:copy>
</xsl:template>

This works for me, but isn't there more elegant way to express "apply transformation to given elements, and apply-templates to the rest"? Now, I'm kind of stuck with this expression *[not(starts-with(name(), 'dob'))] which is not that bad, but if the names of "DOB" attributes change, I'll have to fix this too.

Upvotes: 0

Views: 125

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

Here's one way you could look at it:

<xsl:stylesheet version="2.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="dob_year">
    <dob>
        <xsl:value-of select="(., ../dob_month, ../dob_date)" separator="-"/>
    </dob>
</xsl:template>

<xsl:template match="dob_month|dob_date"/>

</xsl:stylesheet>

Here's another:

<xsl:stylesheet version="2.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="contact_info">
    <xsl:copy>
        <xsl:copy-of select="name|gender"/>
        <dob>
            <xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
        </dob>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Edit:

In order to enumerate only the DOB elements and to do so only once, you could do:

<xsl:template match="contact_info">
    <xsl:variable name="dob-fields" select="(dob_year, dob_month, dob_date)" />
    <xsl:copy>
        <xsl:apply-templates select="* except $dob-fields"/>
        <dob>
            <xsl:value-of select="$dob-fields" separator="-"/>
        </dob>
    </xsl:copy>
</xsl:template>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167571

Change

<xsl:template match="contact_info">
    <xsl:copy>
        <dob>
            <xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
        </dob>
    </xsl:copy>
</xsl:template>

to

<xsl:template match="contact_info">
    <xsl:copy>
        <xsl:apply-templates select="dob_year/preceding-sibling::node()"/>
        <dob>
            <xsl:value-of select="(dob_year, dob_month, dob_date)" separator="-"/>
        </dob>
        <xsl:apply-templates select="dob_date/following-sibling::node()"/>
    </xsl:copy>
</xsl:template>

Upvotes: 1

Related Questions