0

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,'&#xA;')"/>
</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
6
  • 2
    Add your best attempt up to now Commented Sep 10, 2015 at 15:11
  • XSLT is not a best tool to generate plain text files, and also I see no logic in output. Commented Sep 10, 2015 at 15:23
  • 1
    @Niki The input that you show us uses a prefix a: without binding it to a namespace. That's not allowed in XML and a document like this cannot be processed at all. Commented Sep 10, 2015 at 20:38
  • 1
    Also please post the exact expected output in full. Commented Sep 10, 2015 at 20:44
  • 2
    @RudolfYurgenson, why not? It may not be the best tool for parsing text files, but it is certainly very suitable to create text files, in fact, it is one of its core strengths. Commented Sep 10, 2015 at 21:04

2 Answers 2

1

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() &lt; $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>&#x9;</xsl:text>
    </xsl:for-each>


    <xsl:text>&#x0D;&#x0A;</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 &lt; $num]">
              <xsl:text>-&#x09;</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>&#x9;</xsl:text>
    </xsl:for-each>

      <xsl:choose>
          <xsl:when test="$level &lt; $maxlevel and *[*]">
              <xsl:apply-templates select="*[*]">
                  <xsl:with-param name="level" select="$level + 1"/>
        </xsl:apply-templates>
          </xsl:when>
          <xsl:when test="$level &lt; $maxlevel and not(*[*])">
              <xsl:for-each select="$cols/col[num &gt; $num + count($cols/col[level = $level and parent = name(current())]) - 1]">
                  <xsl:text>-&#x09;</xsl:text>
              </xsl:for-each>
              <xsl:text>&#x0D;&#x0A;</xsl:text>
          </xsl:when>
          <xsl:otherwise>
              <xsl:text>&#x0D;&#x0A;</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 :)

Sign up to request clarification or add additional context in comments.

4 Comments

A +1 for the effort and for the creativity to use keys that combine parent and current node names. I'm not sure it is the easiest approach to the task at hand, but it certainly looks interesting :)
"I wrote a very complex xslt, that produces almost exactly the required output" How about a simple one that produces it exactly? xsltransform.net/bdxtrf
@michael.hor257k too simple :) I don't question the simplicity of writing XSLT to produce the desired result in the edited question. The task in the original question was pretty vague, so I made a generic solution for converting xml to tabular text. Notice, that my solution is schema-agnostic. I did it for the sake of challenge and as a reply to Rudolf Yurgenson
@ideafixxxer The only thing I disagree with is this: "The task in the original question was pretty vague",
1

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>&#xA;</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>&#xA;</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>&quot;</xsl:text>
                <xsl:value-of select="$value"/>
                <xsl:text>&quot;</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.

2 Comments

your example is working but still not getting the desired results. I want to show you my output but dont know how i can do it here.
@Niki edit your question and show us desired output. Perhaps values of the Reference nodes must be not in one row as I have done, but in a few lines?

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.