2

I am new to XSLT and I am asking for some help. I need to transform simple XML file to another XML. Here's the first one:

<?xml version="1.0" encoding="utf-8"?>
<library xmlns="http://example.net/library/1.0">
    <authors>
        <author id="a1">
            <name>John</name>
            <surname>Applesed</surname>
            <born>1979-11-11</born>
        </author>
        <author id="a2">
            <name>Krzysztof</name>
            <surname>Habdank</surname>
            <born>1965-12-12</born>
        </author>
        <author id="a3">
            <name>Paulo</name>
            <surname>Coelho</surname>
            <born>1915-06-17</born>
        </author>
        <author id="a4">
            <name>Mikołaj</name>
            <surname>Kopernik</surname>
            <born>1473-02-19</born>
            <died>1543-05-24</died>
        </author>
    </authors>
    <books>
        <book id="b1" author-id="a1">
            <title>Missing opportunity</title>
            <published>1992</published>
            <isbn>978-3-16-148410-0</isbn>
        </book>
        <book id="b2" author-id="a4">
            <title>O obrotach sfer niebieskich</title>
            <published>1543</published>
        </book>
    </books>
</library>

Here's the format I want to get:

<?xml version="1.0" encoding="utf-8"?>
<books xmlns="http://example.net/books/1.0" xmlns:a="http://example.net/author/1.0">
    <book>
        <a:author>
            <a:name>John</a:name>
            <a:surname>Applesed</a:surname>
        </a:author>
        <title>Missing opportunity</title>
    </book>
    <book>
        <a:author>
            <a:name>Mikołaj</a:name>
            <a:surname>Kopernik</a:surname>
        </a:author>
        <title>O obrotach sfer niebieskich</title>
    </book>
</books>

And here's what i managed to code:

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

<xsl:template match="/">
  <books xmlns="http://example.net/books/1.0" xmlns:a="http://example.net/author/1.0">
     <xsl:for-each select="library/authors/author[@id='id']">
       <book>
         <name><xsl:value-of select="@name"/></name>
         <surname><xsl:value-of select="@surname"/></surname>
       </book>
     </xsl:for-each>
  </books>
</xsl:template>

</xsl:stylesheet>

Unfortunately, all I got is:

<?xml version="1.0" encoding="UTF-8"?>
<books xmlns="http://example.net/books/1.0" xmlns:a="http://example.net/author/1.0" />

My source of knowledge: https://msdn.microsoft.com/en-us/library/ms766462(v=vs.85).aspx

I was checking effect on this site:

http://www.freeformatter.com/xsl-transformer.html#ad-output

Thanks for your time and effort.

Szymek
  • 69
  • 9
  • See: http://stackoverflow.com/questions/34758492/xslt-transform-doesnt-work-until-i-remove-root-node/34762628?s=4|0.4019#34762628 – michael.hor257k Jun 30 '16 at 14:22

1 Answers1

0

The stylesheet that produces the desired output looks like this:

<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:l="http://example.net/library/1.0"
    xmlns:a="http://example.net/author/1.0"
    xmlns="http://example.net/books/1.0"
    exclude-result-prefixes="l"
>
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/l:library">
        <xsl:apply-templates select="l:books" />
    </xsl:template>

    <xsl:template match="l:books">
        <books>
            <xsl:apply-templates select="l:book" />
        </books>
    </xsl:template>

    <xsl:template match="l:book">
        <xsl:variable name="author" select="//l:author[@id = current()/@author-id]" />
        <book>
            <a:author>
                <a:name><xsl:value-of select="$author/l:name" /></a:name>
                <a:surname><xsl:value-of select="$author/l:surname" /></a:surname>
            </a:author>
            <title><xsl:value-of select="l:title" /></title>
        </book>
    </xsl:template>
</xsl:stylesheet>

Notes:

  • Every XML namespace you intend to use (input or output) should be declared at the <xsl:stylesheet>.
  • One of them can become the default namespace (here it's http://example.net/books/1.0), which can be recognized by the fact that no prefix is registered. Every unprefixed element in the stylesheet will be in this namespace.
  • All other XML namespaces need a prefix, here I chose l for the library namespace and a for the author namespace, but you can use any prefix you like.
  • All namespaces that should not appear in the output document can be suppressed via exclude-result-prefixes
  • You can store nodes in variables as shown with the $authors variable.
  • The current() function lets you refer inside XPath expressions to the node that the XSLT processor is currently working on.
  • Nodes that are in a default namespace in the input document must be referred to using a prefix, like in this case with l.
  • Try to prefer <xsl:template>/<xsl:apply-templates> when writing stylesheets.
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thanks for the answer! – Szymek Jun 30 '16 at 15:28
  • You're welcome. (BTW, in contrast to the vast majority of new users on SO you did literally everything properly in your question. Complete problem description, valid sample input, your attempt so far, desired output, cross-links to resources you have used, proper markdown format. That's worth mentioning because it happens much to rarely.) – Tomalak Jun 30 '16 at 15:33