Solved

XSLT Identity Transformation problem

Posted on 2006-06-13
10
480 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

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

 
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

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
XSL - change date format 3 49
Transform XML to Excel using XSL 1 33
XML to Excel using XSL - formatting 3 37
write xml in vb.net 2 32
The Client Need Led Us to RSS I recently had an investment company ask me how they might notify their constituents about their newsworthy publications.  Probably you would think "Facebook" or "Twitter" but this is an interesting client.  Their cons…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

861 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