1

I have problems with converting a xml file to a xml with XSLT. I can seem to get the attributes of the first node but hen I can't get the nodes named <attribute> and its attributes and value, and in some case the nodes <attribute> has a node named <codedvalue> which has attributes and values I need:

Here is an example of a file (EDIT: whitespace added for legibility):

<?xml version='1.0' encoding='UTF-8'?>
<arb:result xmlns:arb="urn::codeservice">
 <arb:document 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="urn::codeservice prod_code.xsd">
  <header>Produced by CodeRows 4.2</header>
   <body>
    <termsystem id="1" begindate="1970-01-01T00:00:01" 
      expirationdate="2099-12-31T23:59:59" 
      lastmodifieddate="2012-10-05T09:52:02.35522" 
      lastmodifiedby="Admin">
     <attribute type="longname" datatype="ST" 
       language="fi">1</attribute>
     <attribute type="status" datatype="ST">1</attribute>
     <attribute type="codetype" datatype="ST">1</attribute>
     <attribute type="relatesto" datatype="ST">BASE</attribute>
     <attribute type="relatestoname" datatype="ST">BASE</attribute>
     <attribute type="hierarchical" datatype="ST">0</attribute>
     <termitementry id="1010110" language="fi" 
      createdate="2007-10-25T15:24:17.0" 
      begindate="2003-01-01T00:00:01.0" 
      expirationdate="2004-12-31T23:59:59.0" 
      lastmodifieddate="2007-10-25T17:06:25.0" 
      lastmodifiedby="Admin">
     <attribute type="status" datatype="ST">1</attribute>
     <attribute type="shortname" datatype="ST" language="fi">Whey protein</attribute>
     <attribute type="longname" datatype="ST" language="fi" />
     <attribute type="abbreviation" datatype="ST" 
       language="fi">Raspberry</attribute>
     <attribute type="hierarchylevel" datatype="ST">0</attribute>
     <attribute type="parentid" datatype="ST" language="fi" />
     <attribute type="description" datatype="ST" language="fi" />
     <attribute type="owner" datatype="ST" 
       language="sv">Admin</attribute>
     <attribute type="externallink" 
      datatype="CV" 
      begindate="2003-01-01T00:00:01.0" 
      expirationdate="2005-12-31T23:59:59.0"
      ><codedvalue code="08" 
          codesystem="PUN" 
          codesystemversion="1" 
          referenceid="BASEpowder" /></attribute>
     <attribute type="externallink" 
       datatype="CV" 
       begindate="2001-01-01T00:00:01.0" 
       expirationdate="2006-12-31T23:59:59.0"
       ><codedvalue code="80112" 
          codesystem="PROTEIN" 
          codesystemversion="1" 
          referenceid="BASEprotein" /></attribute>
     <attribute type="externallink" 
       datatype="CV" 
       begindate="2003-01-01T00:00:01.0" 
       expirationdate="2008-01-31T23:59:59.0"
       ><codedvalue code="03" 
          codesystem="REF" 
          codesystemversion="1" 
          referenceid="BASEref" /></attribute>
</termitementry
></termsystem
></body
></arb:document
></arb:result>

The XSLT I am using is:

<?xml version="1.0" encoding="utf-8" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    
    <xsl:template match="termitementry">
        <xsl:element name="CodeRow">
        
            <xsl:element name="Code">
                <xsl:apply-templates select="@id" />
            </xsl:element>
            
            <xsl:element name="Begindate">
                <xsl:apply-templates select="@begindate" />
            </xsl:element>
            
            <xsl:element name="Expirationdate">
                    <xsl:apply-templates select="@expirationdate" />
            </xsl:element>
            
            <xsl:element name="Lastmodifiedby">
                    <xsl:apply-templates select="@lastmodifiedby" />
            </xsl:element>
            
            
        </xsl:element> 
    </xsl:template> 
    
    <xsl:template match="@id">
        <xsl:value-of select="." />
    </xsl:template>
    
    <xsl:template match="@begindate">
        <xsl:value-of select="." />
    </xsl:template>

    <xsl:template match="@expirationdate">
        <xsl:value-of select="." />
    </xsl:template> 
    
    <xsl:template match="@lastmodifiedby">
        <xsl:value-of select="." />
    </xsl:template>
    
</xsl:stylesheet>

and with this I only seem to get the attributes of the first node <termitementry> but not anything after + that I get some crap, I am using http://chris.photobooks.com/xml/default.htm to test things out

<transformiix:result>
    Produced by CodeRows 4.2
    
    
    1
    1
    1
    BASE
    BASE
    0
    <CodeRow>
        <Code>
            1010110
        </Code>
        <Begindate>
            2003-01-01T00:00:01.0
        </Begindate>
        <Expirationdate>
            2004-12-31T23:59:59.0
        </Expirationdate>
        <Lastmodifiedby>
            Admin
        </Lastmodifiedby>
    </CodeRow>
</transformiix:result>

Can someone help how to get out the rest?

I want to get a xml with a basic structure of:

<coderow>
     <Code>
          1010110
     </Code>
    <Begindate>
        2003-01-01 (only want the 10 first digits of 2003-01-01T00:00:01.0, preferably I would just to have the digits so it looks like 20030101)
    </Begindate>
    <Shortname>
        Whey protein
    </Shortname>
    <BASEpowder>
        08
    <BASEpowder>
    <BASEprotein>
        80112
    </BASEprotein>

</CodeRow>

And how come I get out "Produced by CodeRows 4.2 etc.." where I can't see I have made any template for it?

1
  • 2
    Question not clear, please update output with actual values in input xml. Commented Oct 26, 2012 at 7:54

1 Answer 1

2

In answer to your question "how come I get out 'Produced by CodeRows 4.2 etc..' where I cant see I have made any template for it?" the answer is because you haven't made any templates for the other elements, XSLT will use a default template match. This default behaviour will either process all the nodes child nodes, or in the case or text nodes, it will output these as text.

XSLT will start off my looking off for a template that matches the top-level document element, and as you haven't specified a template match, the default behaviour kicks in. You first template matches termitementry and by the time it finds this it will already have performed the default match for all the parent elements.

To stop this happening, and jump straight to the termitemtentry node, You should do something like this

<xsl:template match="/"> 
  <xsl:apply-templates select="//termitementry" />
</xsl:template>

Also, note your existing code to turn attributes into elements can be simplified. Instead of doing this

<xsl:element name="Code">
    <xsl:apply-templates select="@id" />
</xsl:element>

You can just do this

<Code>
   <xsl:value-of select="@id"/>
</Code>

This means you can remove the templates that match all the attributes.

You probably only need a template match for your 'external' link elements, where you can do something like this

<xsl:template match="attribute[@type='externallink']">
   <xsl:element name="{codedvalue/@referenceid}">
      <xsl:value-of select="codedvalue/@code"/>
   </xsl:element>
</xsl:template>

Here is the full XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:apply-templates select="//termitementry"/>
   </xsl:template>

   <xsl:template match="termitementry">
      <CodeRow>
         <Code>
            <xsl:value-of select="@id"/>
         </Code>
         <Begindate>
            <xsl:value-of select="@begindate"/>
         </Begindate>
         <Expirationdate>
            <xsl:value-of select="@expirationdate"/>
         </Expirationdate>
         <Lastmodifiedby>
            <xsl:value-of select="@lastmodifiedby"/>
         </Lastmodifiedby>
         <xsl:apply-templates select="attribute[@type='externallink']"/>
      </CodeRow>
   </xsl:template>

   <xsl:template match="attribute[@type='externallink']">
      <xsl:element name="{codedvalue/@referenceid}">
         <xsl:value-of select="codedvalue/@code"/>
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>

When applied to your sample XML, the following is output

<CodeRow>
   <Code>1010110</Code>
   <Begindate>2003-01-01T00:00:01.0</Begindate>
   <Expirationdate>2004-12-31T23:59:59.0</Expirationdate>
   <Lastmodifiedby>Admin</Lastmodifiedby>
   <BASEpowder>08</BASEpowder>
   <BASEprotein>80112</BASEprotein>
   <BASEref>03</BASEref>
</CodeRow>
Sign up to request clarification or add additional context in comments.

4 Comments

I <3 U, I had not grapsed how the xslt paraser goes through the document until your explantion, now I wonder if I want to parse the dates so it only takes out the first digits I need to make a templete for it right? Want that 2003-01-01T00:00:01.0 will only be 20030101
That is not too hard to do. In XSLT1.0 you can use a combination of 'substring' and 'translate', for example. If you can't work it out, feel free to ask a whole new question about date-formatting, and I am sure it will be answered quickly :)
One more thing, which XSLT-version to use, XSLT1.0 or XSLT2.0 ?
If you can use XSLT2.0, then you should! The above solution will work in both though.

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.