We help IT Professionals succeed at work.

XSLT Identity Transformation problem

mattbelfast23
on
499 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
Comment
Watch Question

Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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!
Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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>


Author

Commented:
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?
Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
Can you show the exact code for the addition you are using?
Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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

Author

Commented:
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
Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
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>
Information Architect
CERTIFIED EXPERT
Top Expert 2006
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Author

Commented:
Thanks very much for your help Gertone.  Much appreciated.
Gertone (Geert Bormans)Information Architect
CERTIFIED EXPERT
Top Expert 2006

Commented:
welcome
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.