Solved

XSLT Identity Transformation problem

Posted on 2006-06-13
10
479 Views
Last Modified: 2008-03-10
Hi all,

Any help on the following problem would be greatly appreciated.  

I need to be able to transform an XML file through several XSL files (Pipelining) whilst maintaining the basic structure of the XML to produce an XML output file.  There will only be small changes made to the XML in these XSL Stylesheets so its important to just pass through most of the XML without changing it at all.  Hence i have used

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

What XSL code can I add to this stylesheet to update/add or delete some of the elements whilst passing through the rest of the XML unchanged (see below for example) ?

Input XML File:

<?xml version="1.0" standalone="yes"?>
<DataSet xmlns="http://tempuri.org/DataSet.xsd">
  <item>    
    <Key>89308F80-A3DF-4B61-8419-3EE6FF0D6E01</Key>
    <TableName>TEST1</TableName>
    <KeyPart1>1</KeyPart1>
  </Item>
  <item>    
    <Key>89308F80-A3DF-4B61-8419-3EE6FF0D6E02</Key>
    <TableName>TEST2</TableName>
    <KeyPart1>1</KeyPart1>
  </item>
</DataSet>

XSL 1:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
      <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>  
</xsl:template>

<!-- Insert a new element node into the Item node ???? -->
<xsl:template match="DataSet">
      <xsl:for-each select="Item">
            <NewElement>Test Info</NewElement>
      </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

XSL 2:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
      <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>  
</xsl:template>

<!-- Change the TableName value ???? -->
<xsl:template match="DataSet/Item">
      <xsl:for-each select="TableName">
            <TableName>Change the TableName Value</TableName>
      </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Thanks for your help

Matt
0
Comment
Question by:mattbelfast23
  • 7
  • 3
10 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16893752
Hi mattbelfast23,

since this is a very hierarchical approach,
you would better tackle this in the smallest particle template

so instead of
<xsl:template match="DataSet/Item">
      <xsl:for-each select="TableName">

you should have
    <xsl:template match="DataSet/Item/TableName">
           <TableName>Change the TableName Value</TableName>
    </xsl:template>

that is a lot safer as regards maintaining whitespace and all that
(so don't mix with for-each)


Cheers!
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16893985
mattbelfast23,
> <xsl:template match="node()|@*">
>      <xsl:copy>
>           <xsl:apply-templates select="node()|@*"/>
>      </xsl:copy>  
> </xsl:template>

I by the way prefer this style for identity copying
 
 <xsl:template match="node()|text()">
      <xsl:copy>
           <xsl:copy-of select="@*" />
           <xsl:apply-templates select="node()|text()"/>
      </xsl:copy>  
 </xsl:template>

gives me control from within the element
can't explain that this would be better or not
but I find it more convenient to change specific processing from this form
(means no templates for attributes)
your style would be better if you want to do something specific to an attribute with a certain name,
regardless of the element it is attached to

anyway, just a thought

for your XSL1 this would be the result

<xsl:template match="DataSet/Item">
      <xsl:copy>
           <xsl:copy-of select="@*" />
            <NewElement>Test Info</NewElement>
          <xsl:apply-templates select="node()|text()"/>
      </xsl:copy>  
</xsl:template>

would put a new element BEFORE the other children of Item

If you by the way want to delete a subelement in one of your pipes
you need to do this (eg. Keypart1 in Item)

<xsl:template match="Item">
      <xsl:copy>
           <xsl:copy-of select="@*" />
           <xsl:apply-templates select="node()[not(name() = 'KeyPart1')]|text()"/>
      </xsl:copy>  
 </xsl:template>


0
 

Author Comment

by:mattbelfast23
ID: 16895078
Hi Gertone,

Your suggestion for Deletion worked a treat, however when i tried the Addition (XSL1) of a new element it was returning the XML like this:

<?xml version="1.0" encoding="utf-8"?>13489308F80-A3DF-4B61-8419-3EE6FF0D6E01TEST113489308F80-A3DF-4B61-8419-3EE6FF0D6E02TEST213489308F80-A3DF-4B61-8419-3EE6FF0D6E03TEST3

Any ideas why this may be happening?
0
Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16897662
Can you show the exact code for the addition you are using?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16897717
This is what you would need:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://tempuri.org/DataSet.xsd"
    xmlns:uri="http://tempuri.org/DataSet.xsd"
    exclude-result-prefixes="uri" version="1.0">
    <xsl:template match="node()|text()">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
    <xsl:template match="uri:DataSet/uri:item">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <uri:NewElement>Test Info</uri:NewElement>
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
 </xsl:stylesheet>

note the namespace allocation, taken from your example XML
also note that case is important in element names
0
 

Author Comment

by:mattbelfast23
ID: 16901009
Hi Gertone,

Thats a great help, thanks very much.  For an extra 100 points could you please help me with this problem that follows on from your above solution?  I essentially need to match an DataSet/Item on a TableName value (TEST1).  If the TableName exists I then need to create a new DataSet/Item element at the same level as the others.  Is this possible?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://tempuri.org/DataSet.xsd"
    xmlns:uri="http://tempuri.org/DataSet.xsd"
    exclude-result-prefixes="uri" version="1.0">
    <xsl:template match="node()|text()">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
    <xsl:template match="uri:DataSet/uri:item">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <uri:NewElement>Test Info</uri:NewElement>
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>

   <!-- If the TableName = TEST1 create a new ITEM element at the same level as the others -->
    <xsl:template match="uri:DataSet/uri:item/uri:TableName[text()='TEST1']">
        <xsl:copy>
            <xsl:copy-of select="@*" />

<!-- I need this to be at the same level as the other item elements -->

            <item>      
            <Key>89308F80-A3DF-4B61-8419-3EE6FF0D6E06</Key>
            <KeyPart1>1</KeyPart1>
            <TableName>TESTNEWITEM</TableName>
            </item>

            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
 </xsl:stylesheet>

Thanks for any help

Matt
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16901052
Matt,

what about this?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://tempuri.org/DataSet.xsd"
    xmlns:uri="http://tempuri.org/DataSet.xsd"
    exclude-result-prefixes="uri" version="1.0">
    <xsl:template match="node()|text()">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
    <xsl:template match="uri:DataSet/uri:item">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <uri:NewElement>Test Info</uri:NewElement>
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
     <!-- If the TableName = TEST1 create a new ITEM element at the same level as the others -->
    <xsl:template match="uri:DataSet[uri:item/uri:TableName[text()='TEST1']]">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <item>    
                <Key>89308F80-A3DF-4B61-8419-3EE6FF0D6E06</Key>
                <KeyPart1>1</KeyPart1>
                <TableName>TESTNEWITEM</TableName>
            </item>
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
</xsl:stylesheet>
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 400 total points
ID: 16901077
If you want to control the position closer
(eg. needs to be the item after the item that contains the TEST1)
you could use a test

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://tempuri.org/DataSet.xsd"
    xmlns:uri="http://tempuri.org/DataSet.xsd"
    exclude-result-prefixes="uri" version="1.0">
    <xsl:template match="node()|text()">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>  
    </xsl:template>
    <xsl:template match="uri:DataSet/uri:item">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <uri:NewElement>Test Info</uri:NewElement>
            <xsl:apply-templates select="node()|text()"/>
        </xsl:copy>
        <xsl:if test="uri:TableName/text() ='TEST1'">
            <item>    
                <Key>89308F80-A3DF-4B61-8419-3EE6FF0D6E06</Key>
                <KeyPart1>1</KeyPart1>
                <TableName>TESTNEWITEM</TableName>
            </item>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
0
 

Author Comment

by:mattbelfast23
ID: 16904460
Thanks very much for your help Gertone.  Much appreciated.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 16904878
welcome
0

Featured Post

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

The Problem How to write an Xquery that works like a SQL outer join, providing placeholders for absent data on the outer side?  I give a bit more background at the end. The situation expressed as relational data Let’s work through this.  I’ve …
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
Established in 1997, Technology Architects has become one of the most reputable technology solutions companies in the country. TA have been providing businesses with cost effective state-of-the-art solutions and unparalleled service that is designed…
Email security requires an ever evolving service that stays up to date with counter-evolving threats. The Email Laundry perform Research and Development to ensure their email security service evolves faster than cyber criminals. We apply our Threat…

778 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question