I have two XML files:
data1.xml
<?xml version="1.0" encoding="UTF-8"?>
<tables>
<table>
<row>
<cell colname="1">A</cell>
<cell colname="2">
<carType>Sedan</carType>
<gasType>Gasoline</gasType>
</cell>
<cell colnane="4">B</cell>
<cell colname="5">C</cell>
</row>
<row>
<cell colname="1">A1</cell>
<cell colname="2">
<carType>Truck</carType>
<gasType>Diesel</gasType>
</cell>
<cell colname="4">B1</cell>
<cell colname="5">C1</cell>
</row>
</table>
</tables>
data2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tables>
<table>
<row>
<cell colname="1">A</cell>
<cell colname="2">
<carType>SedanXYZ</carType>
<gasType>GasolineXYZ</gasType>
</cell>
<cell colname="4">B</cell>
<cell colname="5">C</cell>
</row>
<row>
<cell colname="1">A2</cell>
<cell colname="2">
<carType>Motorcycle</carType>
<gasType>Gasoline</gasType>
</cell>
<cell colname="4">U</cell>
<cell colname="5">Z</cell>
</row>
<row>
<cell colname="1">A1</cell>
<cell colname="2">
<carType>TruckXYZ</carType>
<gasType>DieselXYZ</gasType>
</cell>
<cell colname="4">B1</cell>
<cell colname="5">C1</cell>
</row>
</table>
</tables>
Basically I want to copy whatever contained in colname="2" from data2.xml to data1.xml and keep the rest of the data in data1.xml the same. The keys to find the equality are colname="4" and colname="5". My XSLT looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" version="1.0" encoding="iso-8859-1" indent="yes"/>
<xsl:param name="doc2"/>
<xsl:template match="/">
<xsl:message>Starting off</xsl:message>
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<xsl:template match="cell[@colname='2']">
<xsl:variable name="key-value">
<xsl:call-template name="key-value"/>
</xsl:variable>
<xsl:for-each select="document($doc2)//row">
<xsl:if test="key('keyx', $key-value)">
<xsl:copy-of select="cell[@colname='2']"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- Just copy any other elements, attributes, etc. -->
<xsl:template match="@*|node()" >
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="keyx" match="row"
use="concat(cell[@colname='4'], cell[@colname='5'])"/>
<!-- This template retrives the key value for an element -->
<xsl:template name="key-value">
<xsl:value-of select="concat(../cell[@colname='4'],../cell[@colname='5'])"/>
</xsl:template>
</xsl:stylesheet>
The result I'm expecting:
<?xml version="1.0" encoding="iso-8859-1"?>
<tables>
<table>
<row>
<cell colname="1">A</cell>
<cell colname="2">
<carType>SedanXYZ</carType>
<gasType>GasolineXYZ</gasType>
</cell>
<cell colname="4">B</cell>
<cell colname="5">C</cell>
</row>
<row>
<cell colname="1">A1</cell>
<cell colname="2">
<carType>TruckXYZ</carType>
<gasType>DieselXYZ</gasType>
</cell>
<cell colname="4">B1</cell>
<cell colname="5">C1</cell>
</row>
</table>
</tables>
BUT I'm getting incorrect output like this:
<?xml version="1.0" encoding="iso-8859-1"?>
<tables>
<table>
<row>
<cell colname="1">A</cell>
<cell colname="2">
<carType>SedanXYZ</carType>
<gasType>GasolineXYZ</gasType>
</cell>
<cell colname="2">
<carType>Motorcycle</carType>
<gasType>Gasoline</gasType>
</cell>
<cell colname="2">
<carType>TruckXYZ</carType>
<gasType>DieselXYZ</gasType>
</cell>
<cell colname="4">B</cell>
<cell colname="5">C</cell>
</row>
<row>
<cell colname="1">A1</cell>
<cell colname="2">
<carType>SedanXYZ</carType>
<gasType>GasolineXYZ</gasType>
</cell>
<cell colname="2">
<carType>Motorcycle</carType>
<gasType>Gasoline</gasType>
</cell>
<cell colname="2">
<carType>TruckXYZ</carType>
<gasType>DieselXYZ</gasType>
</cell>
<cell colname="4">B1</cell>
<cell colname="5">C1</cell>
</row>
</table>
</tables>
So several questions:
- What's wrong with my XSLT ?
What's the technique to debug the key calls ?.
Thanks you!.
John
What's wrong with your code: you're using key() without the third argument. The third argument tells the system which document you want to search. If you omit it, it searches the document containing the context node, whereas you want to search the "other" document. With this kind of task it's best to have two global variables containing the two document nodes, so you can switch readily between them.
Personally I tackle this kind of merging task using xsl:for-each-group. In the select attribute of for-each-group, select all the relevant elements from both documents. In the body of for-each-group, if current-group() contains a single element, output that one, otherwise decide which one to output based on a test such as
root(.) is $doc-two
.I think you cannot use the key function here, because you need to create a key set on $doc2 and XML Spy does not allow me to do so. And the key on the source XML will not suffice.
So I wrote another solution for you, not using key at all:
Oh, and by the way, you have a couple of
colnane
typo's in the source files.EDIT forgot about the third argument in the key function :-(. So adding
and changing the for-each to
will achieve your objectives.
EDIT 2
OK the above replaces colname="2" cells, but does not leave data1 ones when no data2 matches are found.
Use the following instead: