tags:

views:

89

answers:

1

Hi,

I am trying to learn XSLT but I work best by example. I want to perform a trivial schema to schema transformation. How do I perform this transformation in only one pass (my current solution uses two passes and loses the original order of customers)?

From:

<?xml version="1.0" encoding="UTF-8"?>
<sampleroot>

<badcustomer>
    <name>Donald</name>
    <address>Hong Kong</address>
    <age>72</age>
</badcustomer>

<goodcustomer>
    <name>Jim</name>
    <address>Wales</address>
    <age>22</age>
</goodcustomer>

<goodcustomer>
    <name>Albert</name>
    <address>France</address>
    <age>51</age>
</goodcustomer>

</sampleroot>

To :

<?xml version="1.0" encoding="UTF-8"?>
<records>

<record id="customer">
    <name>Donald</name>
    <address>Hong Kong</address>
    <age>72</age>
    <customertype>bad</customertype>
</record>

<record id="customer">
    <name>Jim</name>
    <address>Wales</address>
    <age>22</age>
    <customertype>good</customertype>
</record>

<record id="customer">
    <name>Albert</name>
    <address>France</address>
    <age>51</age>
    <customertype>good</customertype>
</record>

</records>

I already solved this a bad way (I lose the order of customers and I think that I have to parse the file multiple times:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/sampleroot">

    <records>

        <xsl:for-each select="goodcustomer">
            <record id="customer">
                <name><xsl:value-of select="name" /></name>
                <address><xsl:value-of select="address" /></address>
                <age><xsl:value-of select="age" /></age>
                <customertype>good</customertype>
            </record>
        </xsl:for-each>

        <xsl:for-each select="badcustomer">
            <record id="customer">
                <name><xsl:value-of select="name" /></name>
                <address><xsl:value-of select="address" /></address>
                <age><xsl:value-of select="age" /></age>
                <customertype>bad</customertype>
            </record>
        </xsl:for-each>

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

Please can someone help me out with the correct XSLT construct where I only have to use a single parse (only one for-each)?

Thanks,

Chris

+1  A: 

It is a good XSLT practice to avoid using <xsl:for-each> as much as possible.

Here is a simple solution, using this principle:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <records>
    <xsl:apply-templates/>
  </records>
 </xsl:template>

 <xsl:template match="badcustomer | goodcustomer">
  <record>
   <xsl:apply-templates/>
   <customertype>
     <xsl:value-of select="substring-before(name(), 'customer')"/>
   </customertype>
  </record>
 </xsl:template>
</xsl:stylesheet>

Do note:

  1. Only templates and <xsl:apply-templates> are used.

  2. The use of the identity rule and its overriding wherever necessary. This is one of the most fundamental XSLT design pattern.

Dimitre Novatchev
Thanks, can I ask for one final piece of information. If I wish to pass through attributes from the original xml schema, how would I adjust the xslt for this to work?<badcustomer> <name myattribute="attributevalue" >Donald</name> <address myattribute2="attributevalue2" >Hong Kong</address> <age>72</age></badcustomer>
Chris
@Chris: Please, ask this as a new question, with properly formatted code/xml. Also, you are using the word "schema" incorrectly. You are actually referrring to the "source XML document" or to an instance of the schema for that document type.
Dimitre Novatchev
Thanks for the information and for correcting my incorrect use of "schema". I figured out how to solve the follow-up question so I won't make a new question now.
Chris