automatix
automatix

Reputation: 14552

XSLT: How to transform a string to multiple elements?

I'm trying to transform this XML document:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<data>
    <entry>
        <databit>30.11.2012</databit>
        <databit>Foo</databit>
        <databit>Key: Value<br />Key: Value<br /> ... Key: Value</databit>
        <databit>some Foo content</databit>
        <databit>more Foo content</databit>
    </entry>
    <entry>
        ...
    </entry>
    ...
</data>

The number of the key-value pairs is variable. The separator is always a <br /> tag.

The output should look like this:

<data>
    <entry>
        <date>30.11.2012</date>
        <title>Foo</title>
        <info>
            <part key="Key" value="Value" />
            <part key="Key" value="Value" />
            <part key="Key" value="Value" />
        </info>
        <databit>some Foo content</databit>
        <databit>more Foo content</databit>
    </entry>
    <entry>
        ...
    </entry>
    ...
</data>

Here is the XSLT document I've written:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <data>
            <xsl:for-each select="/data/entry">
                <entry>
                    <date>
                        <xsl:value-of select="databit[1]"/>
                    </date>
                    <title>
                        <xsl:value-of select="databit[2]"/>
                    </title>
                    <info>
                        <xsl:value-of select="databit[3]"/>
                    </info>
                    <category>
                        <xsl:value-of select="databit[4]"/>
                    </category>
                    <status>
                        <xsl:value-of select="databit[5]"/>
                    </status>
                </entry>
            </xsl:for-each>
        </data>
    </xsl:template>
</xsl:stylesheet>

But I have no idea, how to transform the info.

Upvotes: 0

Views: 129

Answers (1)

jasso
jasso

Reputation: 13986

This stylesheet should create the desired transformation. The stylesheet assumes that key and value are always separated with : and it strips the colon and space character, but no other characters. Use normalize-space() if whitespace removal is needed.

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

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

    <xsl:template match="databit[1]">
        <date>
            <xsl:apply-templates/>
        </date>
    </xsl:template>

    <xsl:template match="databit[2]">
        <title>
            <xsl:apply-templates/>
        </title>
    </xsl:template>

    <xsl:template match="databit[3]">
        <info>
            <xsl:apply-templates/>
        </info>
    </xsl:template>

    <xsl:template match="databit[3]/text()">
        <part key="{substring-before(., ':')}" value="{substring(substring-after(., ':'), 2)}"/>
    </xsl:template>

    <xsl:template match="databit[3]/br"/>

</xsl:stylesheet>

Some points on the stylesheet

  • All elements are copied recursively by using an identity template, unless the element has a template of its own
  • All text nodes in the 3rd <databit> element are expected to be key-value pairs.
  • Attributes in <part> elements are added using an XPath expression inside braces (an attribute value template). Other method to add them is to use <xsl:attribute> element.
  • Element <br> is suppressed from copying by using an empty template.

Upvotes: 2

Related Questions