Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

XSLT Identity Transformation problem

Posted on 2006-06-13
10
Medium Priority
?
485 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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 1600 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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

Introduction In my previous article (http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/SSIS/A_9150-Loading-XML-Using-SSIS.html) I showed you how the XML Source component can be used to load XML files into a SQL Server database, us…
Create a Windows 10 custom Image with custom task bar and custom start menu using XML for deployment.
Want to learn how to record your desktop screen without having to use an outside camera. Click on this video and learn how to use the cool google extension called "Screencastify"! Step 1: Open a new google tab Step 2: Go to the left hand upper corn…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…

597 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