Reputation: 39
Its fair that i put some effort in trying out this myself first, here is my xml code. My xsl works fine but i can only get to the parent node and i dont know how to get the text from the child element(Entries\Reference)
<?xml version="1.0"?>
<Catalog>
<Book id="bk101">
<Author>Garghentini, Davide</Author>
<Title>XML Developer's Guide</Title>
<Genre>Computer</Genre>
<Price>44.95</Price>
<a:Entries>
<a:Reference>
<a:ISBN>a0WER8501d2b60c73567f83</a:ISBN>
<a:Description>Classics</a:Description>
<a:EventDate>2015-08-25T00:00:00</a:EventDate>
<a:UnitCost>750.0000</a:UnitCost>
<a:UnitPrice>1380.0000</a:UnitPrice>
<a:UnitPriceInBaseCurrency>1380.0000</a:UnitPriceInBaseCurrency>
</a:Reference>
<a:Reference>
<a:ISBN>a0cVSFWREW01d2b60c73567f83</a:ISBN>
<a:Description>horror</a:Description>
<a:EventDate>2015-6-25T00:00:00</a:EventDate>
<a:UnitCost>150.0000</a:UnitCost>
<a:UnitPrice>130.0000</a:UnitPrice>
<a:UnitPriceInBaseCurrency>130.0000</a:UnitPriceInBaseCurrency>
</a:Reference>
</a:Entries>
<PublishDate>2000-10-01</PublishDate>
<Description>applications with XML.</Description>
</Book>
<Book id="bk102">
<Author>Garcia, Debra</Author>
<Title>Midnight Rain</Title>
<Genre>Fantasy</Genre>
<Price>5.95</Price>
<PublishDate>2000-12-16</PublishDate>
<Description>A former architect battles corporate zombies.</Description>
</Book>
</Catalog>
This is my xsl:
<?xml version="1.0" encoding="utf-8"?>
<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,Genre,Price,PublishDate,Description
<xsl:for-each select="//Book">
<xsl:value-of select="concat( Author,',',Title,',',Genre,',',Price,',',PublishDate,',',Description,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
and this is the output in csv
Author,Title,Genre,Price,PublishDate,Description
Garghentini, Davide,XML Developer's
Guide,Computer,44.95,2000-10-01,applications with XML.
Garcia, Debra,Midnight Rain,Fantasy,5.95,2000-12-16,A former architect
battles corporate zombies.
But i want to also get the output from the child element so i want to see something like this and also its values.i dont know how i can achieve this.
Author,Title,Genre,Price,PublishDate,Description,ISBN,Description,EventDate,UnitCost
Upvotes: 0
Views: 343
Reputation: 14231
Please, try my code:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:a="your:namespace">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<!-- Create header -->
<xsl:for-each select="//Book[1]/*[namespace-uri() != 'your:namespace']">
<xsl:value-of select="name()"/>
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:for-each select="//Book/a:Entries/a:Reference/*">
<xsl:if test="position() != 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select="local-name()"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
<!-- Create body -->
<xsl:for-each select="//Book">
<xsl:for-each select="./*[namespace-uri() != 'your:namespace']">
<xsl:call-template name="test-comma">
<xsl:with-param name="value" select="."/>
</xsl:call-template>
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:for-each select="a:Entries/a:Reference/*">
<xsl:if test="position() != 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:call-template name="test-comma">
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="test-comma">
<xsl:param name="value"/>
<xsl:choose>
<xsl:when test="contains($value, ',')">
<xsl:text>"</xsl:text>
<xsl:value-of select="$value"/>
<xsl:text>"</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Specify the correct namespace instead of your:namespace
.
Note that the values that contain a comma must be enclosed in quotes to get the correct csv.
Upvotes: 1
Reputation: 474
Well, before you edited your question I wrote a very complex xslt, that produces almost exactly the required output for any xml with similar structure. Just want to leave it here
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="text" />
<xsl:key name="key" match="*" use="concat(name(..),':',name())"/>
<xsl:variable name="headers" select="//*[not(*)][count(key('key', concat(name(..),':',name()))[1]|.) = 1]"/>
<xsl:variable name="colsx">
<xsl:for-each select="$headers">
<xsl:variable name="p" select="position()"/>
<col>
<num>
<xsl:value-of select="position()"/>
</num>
<level>
<xsl:value-of select="count(ancestor::*)-1"/>
</level>
<pos>
<xsl:value-of select="count($headers[position() < $p and count(ancestor::*)=count(current()/ancestor::*)])"/>
</pos>
<name>
<xsl:value-of select="concat(name(..),':',name())"/>
</name>
<parent>
<xsl:value-of select="name(..)"/>
</parent>
</col>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cols" select="msxsl:node-set($colsx)"/>
<xsl:variable name="maxlevel">
<xsl:for-each select="$cols/col">
<xsl:sort select="level" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="level"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:for-each select="$headers">
<xsl:value-of select="concat(name(..),':',name())"/>
<xsl:text>	</xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*/*">
<xsl:with-param name="level" select="1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*[*]">
<xsl:param name="level"/>
<xsl:variable name="children" select="*"/>
<xsl:variable name="num" select="$cols/col[level = $level and parent = name(current())][1]/num"/>
<xsl:if test="position() > 1">
<xsl:for-each select="$cols/col[num < $num]">
<xsl:text>-	</xsl:text>
</xsl:for-each>
</xsl:if>
<xsl:for-each select="$cols/col[level = $level and parent = name(current())]">
<xsl:choose>
<xsl:when test="$children[not(*)][concat(name(..),':',name()) = current()/name]">
<xsl:value-of select="$children[not(*)][concat(name(..),':',name()) = current()/name]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>	</xsl:text>
</xsl:for-each>
<xsl:choose>
<xsl:when test="$level < $maxlevel and *[*]">
<xsl:apply-templates select="*[*]">
<xsl:with-param name="level" select="$level + 1"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$level < $maxlevel and not(*[*])">
<xsl:for-each select="$cols/col[num > $num + count($cols/col[level = $level and parent = name(current())]) - 1]">
<xsl:text>-	</xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
So it produces the following output:
bookinfo:title part:title chapter:title chapter:para
Beginning XML - - -
- First Part What is XML? bla bla
- - Well-Formed XML. bla bla
- Second Part XML Namespaces. bla bla
- - XSLT. bla bla
XML in question was
<?xml version="1.0" encoding="utf-8"?>
<book>
<bookinfo>
<title>Beginning XML</title>
</bookinfo>
<part>
<title>First Part </title>
<chapter>
<title>What is XML?</title>
<para>bla bla</para>
</chapter>
<chapter>
<title>Well-Formed XML.</title>
<para>bla bla</para>
</chapter>
</part>
<part>
<title>Second Part </title>
<chapter>
<title>XML Namespaces.</title>
<para>bla bla</para>
</chapter>
<chapter>
<title>XSLT.</title>
<para>bla bla</para>
</chapter>
</part>
</book>
With the desired result as
Bookinfo:title part:title chapter:title chapter:para
Beginning XML First Part What is XML? bla bla
Well-Formed XML bla bla
This is definitely not the stylesheet that you want to start learning XSLT with :)
Upvotes: 1