Oliver Busse
Oliver Busse

Reputation: 3395

Separate output with XSL

Sorry for the misleading title but I don't know a better expression for it. I am trying to create a XSL which provides 2 different main sections in my output XML for completely different types of data. However, I always get BOTH types of data in EACH template call. I hope you can give me a hint how to achieve this. These are my ingredients:

Source XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <books>
        <book>Xpages</book>
        <book>XML</book>
    </books>

    <dvds>
        <dvd>Star Wars</dvd>
        <dvd>Prometheus</dvd>
    </dvds>
</root>

XSL

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <media>
            <xsl:element name="booklist">
                <xsl:call-template name="books"/>
            </xsl:element>
            <xsl:element name="dvdlist">
                <xsl:call-template name="dvds"/>
            </xsl:element>
        </media>
    </xsl:template>

    <xsl:template name="books" match="books">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template name="dvds" match="dvds">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="books/book" name="book">
        <xsl:value-of select="text()"/>
    </xsl:template>

    <xsl:template match="dvds/dvd" name="dvd">
        <xsl:value-of select="text()"/>
    </xsl:template>
</xsl:stylesheet>

This is what I get

<?xml version="1.0" encoding="UTF-8"?>
<media>
    <booklist>
        Xpages
        XML    
        Star Wars
        Prometheus    
    </booklist>
    <dvdlist>
        Xpages
        XML    
        Star Wars
        Prometheus
    </dvdlist>
</media>

But this is what I want

<?xml version="1.0" encoding="UTF-8"?>
<media>
    <booklist>
        Xpages
        XML
    </booklist>
    <dvdlist>
        Star Wars
        Prometheus
    </dvdlist>
</media>

As you can see it doubles my 2 lists. I guess it's because I use apply-templates inside the two templates but there must be a way to solve this. Any help is much appreciated!

Upvotes: 0

Views: 52

Answers (2)

zx485
zx485

Reputation: 29022

One solution would be this XSL-template:

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

    <xsl:template match="/root">
        <media>
            <xsl:element name="booklist">
                <xsl:apply-templates select="books/book" />
            </xsl:element>
            <xsl:element name="dvdlist">
                <xsl:apply-templates select="dvds/dvd" />
            </xsl:element>
        </media>
    </xsl:template>

    <xsl:template match="book">
        <xsl:text>&#xa;</xsl:text><xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="dvd">
        <xsl:text>&#xa;</xsl:text><xsl:apply-templates/>
    </xsl:template>

</xsl:stylesheet>

Its output is

<?xml version="1.0"?>
<media><booklist>
        Xpages
        XML
    </booklist>
    <dvdlist>
        Star Wars
        Prometheus
    </dvdlist>
</media>

Upvotes: 1

Tim C
Tim C

Reputation: 70598

You start off by doing xsl:call-template, but within that the <xsl:apply-templates /> will select all nodes. At this point you are still positioned on the document node. What actually happens is XSLT's built-in templates apply, which first selects the root node, and then, because you have no template for this, it will select both the books and dvds nodes.

What you need to do is simply target the nodes you are interested in.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <media>
            <xsl:element name="booklist">
                <xsl:apply-templates select="root/books" />
            </xsl:element>
            <xsl:element name="dvdlist">
                <xsl:apply-templates select="root/dvds" />
            </xsl:element>
        </media>
    </xsl:template>

    <xsl:template match="books/book" name="book">
        <xsl:value-of select="text()"/>
    </xsl:template>

    <xsl:template match="dvds/dvd" name="dvd">
        <xsl:value-of select="text()"/>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Related Questions