Reputation: 3395
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
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>
</xsl:text><xsl:apply-templates/>
</xsl:template>
<xsl:template match="dvd">
<xsl:text>
</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
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