XSLT - Transform xml to xml

Hello,

I'm new to using XML and XSLT. I am trying to convert one xml doc into another one, so that just the relevant information is available.

Please see the attached code. Please help me with the code.

Thanks.
FROM VERSION
------------

<RGRS VER="3.2">
        <PARAM name="q" value="rome" original_value="rome"/>
        <RES SN="1" EN="10">
                <R N="1">
                        <MT N="TITLE" V="La Pergola, Rome"/>
                        <MT N="THEME" V="Chef, Fast, Healthy"/>
				   <MT N="AUTHOR1" V="Emily Ruben"/>
				   <MT N="AUTHOR2" V="Stephen King"/>
                </R>
                <R N="2">
                        <MT N="TITLE" V="Metro Cafe"/>
                        <MT N="THEME" V="Make Ahead"/>
                </R>
        </RES>
</RGRS>


TO VERSION
----------

<?xml version="1.0"?>
<catalog>
   <books>
      <book>
         <title>La Pergola, Rome</title>
         <themes>
		  <theme>
			<name>Chef</name>
		  </theme>
		  <theme>
			<name>Fast</name>
		  </theme>
		  <theme>
			<name>Healthy</name>
		  </theme>
         </themes>
         <authors>
		  <author>
			<name>Emily Ruben</name>
		  </author>
		  <author>
			<name>Stephen King</name>
		  </author>
         </authors>
      </book>
      <book id="bk102">
         <title>Metro Cafe</title>
         <themes>
		  <theme>
			<name>Make Ahead</name>
		  </theme>
         </themes>
      </book>
   </books>
</catalog>

Open in new window

sunseshasaiAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
Geert BormansConnect With a Mentor Information ArchitectCommented:
So, here is the XSLT1 version of the stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"></xsl:output>
    <xsl:template match="RGRS">
        <catalog>
            <xsl:apply-templates select="RES"/>
        </catalog>
    </xsl:template>
    <xsl:template match="RES">
        <books>
            <xsl:apply-templates select="R"/>
        </books>
    </xsl:template>
    <xsl:template match="R">
        <book>
            <title>
                <xsl:value-of select="MT[@N = 'TITLE']/@V"/>
            </title>
            <xsl:if test="MT[@N = 'THEME']/@V">
                <themes>
                    <xsl:call-template name="process-themes">
                        <xsl:with-param name="str" select="MT[@N = 'THEME']/@V"/>
                    </xsl:call-template>
                </themes>
            </xsl:if>
            <xsl:if test="MT[starts-with(@N, 'AUTHOR')]">
                <authors>
                    <xsl:apply-templates select="MT[starts-with(@N, 'AUTHOR')]"/>
                </authors>
            </xsl:if>
        </book>
    </xsl:template>
    <xsl:template match="MT[starts-with(@N, 'AUTHOR')]">
        <author>
            <name>
                <xsl:value-of select="@V"/>
            </name>
         </author>
    </xsl:template>
    <xsl:template name="process-themes">
        <xsl:param name="str"/>
        <xsl:choose>
            <xsl:when test="not(contains($str, ','))">
                <theme>
                    <name><xsl:value-of select="normalize-space($str)"/></name>
                </theme>
            </xsl:when>
            <xsl:otherwise>
                <theme>
                    <name><xsl:value-of select="normalize-space(substring-before($str, ','))"/></name>
                </theme>
                <xsl:call-template name="process-themes">
                    <xsl:with-param name="str" select="substring-after($str, ',')"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 
Geert BormansInformation ArchitectCommented:
Since you are learning, I suggest that you start using XSLT2 from the very start.
You can do so by using Saxon
The solution I provide uses one XSLT2 construct, the  "tokenize()"
If you need to bring that back to XSLT1, you will need some recursive programming,
let me know if that is the case

The stylesheet is really straightforward. You will figure out how it works, good luck
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"></xsl:output>
    <xsl:template match="RGRS">
        <catalog>
            <xsl:apply-templates select="RES"/>
        </catalog>
    </xsl:template>
    <xsl:template match="RES">
        <books>
            <xsl:apply-templates select="R"/>
        </books>
    </xsl:template>
    <xsl:template match="R">
        <book>
            <title>
                <xsl:value-of select="MT[@N = 'TITLE']/@V"/>
            </title>
            <themes>
                <xsl:for-each select="tokenize(MT[@N = 'THEME']/@V, ',')">
                    <theme>
                        <name><xsl:value-of select="normalize-space(.)"/></name>
                    </theme>
                </xsl:for-each>
            </themes>
            <xsl:if test="MT[starts-with(@N, 'AUTHOR')]">
                <authors>
                    <xsl:apply-templates select="MT[starts-with(@N, 'AUTHOR')]"/>
                </authors>
            </xsl:if>
        </book>
    </xsl:template>
    <xsl:template match="MT[starts-with(@N, 'AUTHOR')]">
        <author>
            <name>
                <xsl:value-of select="@V"/>
            </name>
         </author>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 
sunseshasaiAuthor Commented:
Hello Gertone,

Thanks for sending the code. I am getting the following errors

Error 1:     No character data is allowed between top-level elements

    <xsl:template match="MT[starts-with(@N, 'AUTHOR')]">
        <author>
            <name>
                <xsl:value-of select="@V"/>
            </name>
         </author>
    </xsl:template>

Error 2: Error in expression tokenize(MT[@N = 'THEME']/@V, ','): Unknown system function: tokenize

            <themes>
                <xsl:for-each select="tokenize(MT[@N = 'THEME']/@V, ',')">
                    <theme>
                        <name><xsl:value-of select="normalize-space(.)"/></name>
                    </theme>
                </xsl:for-each>
            </themes>

Please help. Thanks.
0
[Webinar] Improve your customer journey

A positive customer journey is important in attracting and retaining business. To improve this experience, you can use Google Maps APIs to increase checkout conversions, boost user engagement, and optimize order fulfillment. Learn how in this webinar presented by Dito.

 
Geert BormansInformation ArchitectCommented:
Which processor are you using?
If it doesn't matter to you, I recommend that you use saxon9.2HE (check on sourceforge or on www.saxonica.com)
then this will not happen. As I said this is an XSLT2 stylesheet and can not be processed using an XSLT1 processor
So, explain to me how you want to run the XSLT process

If you can't run an XSLT2 process, then I will change your stylesheet into an XSLT1 stylesheet

I don't understand error1 actualy, there is no data at places it should not be, maybe a bad paste?
0
 
sunseshasaiAuthor Commented:
Hello Gertone,
The solution provided by you, worked well for me. Thanks.
The reason for error 1 is, the blank spaces/tabs in the code. Once I removed them it worked for me.
Thanks again.
0
 
Geert BormansInformation ArchitectCommented:
welcome
0
 
sunseshasaiAuthor Commented:
Hi Gertone,
The solution worked great for me. Only one change needed.

if themes has data we are converting as
         <themes>
            <theme>
               <name>Chef</name>
            </theme>
            <theme>
               <name>Fast</name>
            </theme>
         </themes>

if it's empty it's showing as
         <themes>
            <theme>
               <name/>
            </theme>
         </themes>


If it's empty, Is it possible to change it to
         <themes />

And the same kind of change we need it for <authors> too.
<authors />

Please help. Thanks.
0
 
Geert BormansInformation ArchitectCommented:
I have apparently answered this in your follow up question.
The code for disgarding themes was there for elements that were not present,
I now extended that code for elements that have the right attributes, but have empty attribute values
0
All Courses

From novice to tech pro — start learning today.