3

I am new to XSLT and XPath, and am struggling to write a stylesheet to merge two files.

For this question I'm just going to use a simple example of the concept I am struggling with, instead of my actual data. Let's say I have two xml files that contain separate, but related data. authors.xml and books.xml. authors.xml contains a collection of authors, with some basic information about them, while books.xml contains a collection of books, with information (including the author, importantly) about them.

I want a resulting XML file that contains a collection of authors - but inside each author element, also a collection of books that belong to that author.

The best I have managed so far is to just duplicate the list of books inside each author, and I have a feeling that I may even be taking a completely strange/wrong approach to the problem. I am only beginning to wrap my head around how the stylesheet is processed.

Sample authors.xml:

<authors>
    <author>
        <name>Jane Doe</name>
        <age>25</age>
    </author>
    <author>
        <name>John Smith</name>
        <age>53</age>
    </author>
</authors>

Sample books.xml:

<books>
    <book>
        <title>Flying Kites</title>
        <author>Jane Doe</author>
    </book>
    <book>
        <title>XSLT For Dummies</title>
        <author>John Smith</author>
    </book>
    <book>
        <title>Running Fast</title>
        <author>Jane Doe</author>
    </book>
</books>

Sample output.xml:

<authors>
    <author>
        <name>Jane Doe</name>
        <age>25</age>
        <books>
            <book>
                <title>Flying Kites</title>
            </book>
            <book>
                <title>Running Fast</title>
            </book>
        </books>
    </author>
    <author>
        <name>John Smith</name>
        <age>53</age>
        <books>
            <book>
                <title>XSLT For Dummies</title>
            </book>
        </books>
    </author>
</authors>

I'll also provide my current stylesheet, though I fear it may be on the wrong track:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="ISO-8859-1" indent="yes" method="xml"/>
<xsl:variable name="books" select="document('books.xml')"/>

<xsl:template match="/">
    <authors>
        <xsl:apply-templates select="/authors/author"/>
    </authors>
</xsl:template>

<xsl:template match="author">
    <author>
        <name><xsl:value-of select="./name"/></name>
        <age><xsl:value-of select="./age"/></age>
        <books>
            <xsl:apply-templates select="$books/books/book"/>
        </books>
    </author>
</xsl:template>

<xsl:template match="book">
    <!-- How to only copy if correct author? -->    
    <xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>

I've spent a few hours searching around, but nothing I have found has solved my problem - although what I've found has helped me understand how both XSLT and XPath work a little better.

The question here was helpful, but was mostly about 'tacking on' updated data to an existing file. The more relevant part to me was updating the date, which the supplied answer didn't actually do - and was unable to do it myself.

I also posed my question on /r/learnprogramming, but the only responses I got were basically "XSLT is awful. Run away now."

I seldom ask for help, but I've been stumped on this for a while without even an idea of what approach I should be taking. Any help or advice would be greatly appreciated.

If it is relevant, I am using Saxon 9 to process the xslt.

Community
  • 1
  • 1

1 Answers1

1

You're almost there. To select the books per author, use a predicate in the author template.

<xsl:template match="author">
    <author>
        <xsl:copy-of select="name"/>
        <xsl:copy-of select="age"/>
        <books>
            <xsl:apply-templates select="$books/books/book[author=current()/name]"/>
        </books>
    </author>
</xsl:template>

You can also use copy-of (as shown above) instead of value-of.

If you want to copy the entire node, you don't need to change anything in the book template, but copy-of will also include the author child, so you might want to do it differently:

<xsl:template match="book"> 
    <xsl:copy>
        <xsl:copy-of select="title"/>
    </xsl:copy>
</xsl:template>
helderdarocha
  • 23,209
  • 4
  • 50
  • 65
  • Oh wow. Looks like I was quite close. I guess this was a failing in my understanding of XPath rather than XSLT (where I've been focusing my searching). I should definitely be able to apply this to my actual data. Thanks heaps for this! – Falling Olives May 21 '14 at 02:56