处理两个XML文件时无法使XSLT节点副本生效

时间:2012-03-15 05:17:20

标签: xslt xslt-2.0

我有两个XML文件:

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>

基本上我想将dataname =“2”中包含的内容从data2.xml复制到data1.xml,并保持data1.xml中的其余数据相同。找到相等性的关键是colname =“4”和colname =“5”。我的XSLT看起来像这样:

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

我期待的结果:

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

所以有几个问题:

  1. 我的XSLT出了什么问题?
  2. 调试密钥调用的技巧是什么?。

    谢谢!。

    约翰

2 个答案:

答案 0 :(得分:2)

你的代码出了什么问题:你使用key()而没有第三个参数。第三个参数告诉系统您要搜索哪个文档。如果省略它,它将搜索包含上下文节点的文档,而您要搜索“其他”文档。通过这种任务,最好有两个包含两个文档节点的全局变量,因此您可以在它们之间轻松切换。

我个人使用xsl:for-each-group解决这种合并任务。在for-each-group的select属性中,选择两个文档中的所有相关元素。在for-each-group的主体中,如果current-group()包含单个元素,则输出该元素,否则根据root(.) is $doc-two等测试决定输出哪一个。

答案 1 :(得分:1)

我认为你不能在这里使用密钥功能,因为你需要在$ doc2上创建一个密钥集,而XML Spy不允许我这样做。关于源XML的关键是不够的 所以我为你写了另一个解决方案,而不是使用密钥:

<?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:copy-of select="document('C:\test\data2.xml')"/></xsl:param>

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

    <xsl:template match="cell[@colname='2']">
        <xsl:variable name="keyValue" select="concat(../cell[@colname='4'], ../cell[@colname='5'])"/>
        <xsl:for-each select="$doc2/tables/table/row[concat(cell[@colname='4'], cell[@colname='5']) = $keyValue]">
            <xsl:copy-of select="cell[@colname='2']"/>
        </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:stylesheet>

哦,顺便说一下,源文件中有几个colnane错字。

编辑忘记了关键功能中的第三个参数:-(。所以添加

<xsl:key name="keyx" match="row" use="concat(cell[@colname='4'], cell[@colname='5'])"/>

并将for-each更改为

    <xsl:for-each select="key('keyx', $keyValue, $doc2)">
        <xsl:copy-of select="cell[@colname='2']"/>
    </xsl:for-each>

将实现您的目标。

编辑2

好的,上面替换了colname =“2”单元格,但是当没有找到data2匹配时,不会留下data1。

请改用以下内容:

<?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"/> <!-- supplied in parameter list in specific tool -->
    <xsl:key name="keyx" match="row" use="concat(cell[@colname='4'], cell[@colname='5'])"/>

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

    <xsl:template match="cell[@colname='2']">
        <xsl:variable name="keyValue" select="concat(../cell[@colname='4'], ../cell[@colname='5'])"/>
        <xsl:variable name="doc2Matches" select="key('keyx', $keyValue, document($doc2))"/>
        <xsl:choose>
            <xsl:when test="$doc2Matches">
                <xsl:copy-of select="$doc2Matches[1]/cell[@colname='2']"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </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:stylesheet>