1

I'm creating and XSLT file to display some data from an XML file as CSV. I'm stuck on a particular aspect because I can't find many online examples with similar xml structure.

Given this hypothetical XML:

<collection>
 <book>
  <author> author1name </author>
  <author> author2name </author>
  <title> booktitle </title>
 </book>
 <book>
  <author> authorname </author>
  <title> booktitle </title>
 </book>
</collection>

And the xslt:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" >
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>

<xsl:template match="/">
author,title

<xsl:for-each select="//book">
<xsl:value-of select="concat

(author,',',title',','&#xA;')"/>
</xsl:for-each>

</xsl:template>
</xsl:stylesheet>

Which gives an output of

author, title
author1name, booktitle
authorname, booktitle

Note that there is no author2 included in the output. This is a big loss of data. I've tried to use a nested for loop to cycle through all authors but I've hit too many errors to count.

Can someone suggest a method to produce an output of

author1name;author2name, booktitle

for book one? (where the two authors are separated by semicolon)

Thanks for your help.

2 Answers 2

2

What you could do is create a template that matches author where you simply output the name, and semi-colon preceding it if it is not the first author

<xsl:template match="author">
    <xsl:if test="position() > 1">;</xsl:if>
    <xsl:value-of select="." />
</xsl:template>

Then, to output each book, rather than putting author in the concat statement, use xsl:apply-templates instead

<xsl:apply-templates select="author" />
<xsl:value-of select="concat(',', title,'&#xA;')"/>

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
   <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>

   <xsl:template match="collection">
       <xsl:text>author,title</xsl:text>
       <xsl:value-of select="'&#xA;'" />
      <xsl:apply-templates select="book"/>
   </xsl:template>

   <xsl:template match="book">
      <xsl:apply-templates select="author"/>
      <xsl:value-of select="concat(',', title,'&#xA;')"/>
   </xsl:template>

   <xsl:template match="author">
      <xsl:if test="position() &gt; 1">;</xsl:if>
      <xsl:value-of select="."/>
   </xsl:template>
</xsl:stylesheet>

EDIT: As an aside, in the future, if you were able to use XSLT 2.0, you could make use of the separator attribute of the xsl:value-of operator to achieve the same result:

<xsl:value-of select="author" separator=";" />
Sign up to request clarification or add additional context in comments.

Comments

0

concat() is a string function; you cannot apply it to node-sets. Try instead:

XSLT 1.0

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

<xsl:template match="/">
    <xsl:text>author,title&#10;</xsl:text>
    <xsl:for-each select="collection/book">
        <xsl:for-each select="author">
            <xsl:value-of select="."/>
            <xsl:if test="position()!=last()">
                <xsl:text>;</xsl:text>
            </xsl:if>
        </xsl:for-each>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="title"/>
        <xsl:if test="position()!=last()">
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.