通过合并更新节点而不更改节点顺序?

时间:2014-12-29 22:29:04

标签: xml xslt xpath xslt-1.0

背景

我想通过将新内容合并到现有文件来更新XML文件。除了要合并的内容(文件B)之外,现有文件(A)中的所有内容都应保持不变,在本例中为“描述”节点。

我的问题类似于this one。但是,我在答案中遇到的限制是不一定保留节点顺序。如果可能的话,我希望保留原始节点顺序。我当前的XSL只执行以下操作:

  1. 匹配要合并的描述节点,然后将其复制到其他节点之前。
  2. 然后复制其他所有内容(所有不是描述的内容)。
  3. 我的XSL和XML如下。请注意,我仅限于XSLT 1.0

    XSL

    <xsl:variable name="desc" select="document('doc.out.xml')" />
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="//*[name=$desc//node/@name]">
    
        <xsl:variable name="name" select="current()/name" />    
    
        <xsl:copy>
    
            <xsl:apply-templates select="$desc//node[@name=$name]/description" />   
    
            <xsl:apply-templates select="@*|node()[not(self::description)]" />
    
        </xsl:copy>                             
    
    </xsl:template>
    

    XML A(待更新)

        <renderer>
            <name>key</name>
            <description>
                The key element is an Object Element used to create and
                identify rows.
            </description>
            <foo>blah</foo>
            <fields>
                <field>
                    <name>elements</name>
                    <description>
                        A container for all 'element' nodes.
                    </description>
                    <bar>blah</bar>
                    <factory>
                        <localRenderer>
                            <name>element</name>
                            <description>
                                A primitive object and arrays thereof.
                            </description>
                            <baz>blah</baz>
                            <fields>
                                <field>
                                    <name>name</name>
                                    <description>
                                        Alias by which the element may be referenced.
                                    </description>
                                    <bar>blah</bar>
                                </field>
                                <field>
                                    <name>type</name>
                                    <description>
                                        Type of the data.
                                    </description>
                                    <bar>blah</bar>
                                </field>
                            </fields>
                        </localRenderer>
                    </factory>
                </field>
            </fields>
        </renderer>
    

    XML B(包含要合并到A中的内容)

    <root>
        <node name="key">
            <description>The key element is an Object Element used to create and
                identify rows.Note that object declaration via XML must occur within
                a definitions element. See for more information.</description>
            <subnodes>
                <node name="elements">
                    <description>
                    Hi. This is a  test.
                    </description>
                    <subnodes>
                        <node name="element">
                            <description>A primitive object and arrays thereof. TEST</description>
                            <subnodes>
                                <node name="name">
                                    <description>Alias by which the element may be referenced.
                                    </description>
                                </node>
                                <node name="type">
                                    <description>Type of the data; a primitive object of array
                                        thereof. </description>
                                </node>
                            </subnodes>
                        </node>
                    </subnodes>
                </node>
            </subnodes>
        </node>
    </root>
    

    当前输出

    请注意,描述现在是第一个节点。

        <renderer>
            <description>The key element is an Object Element used to create and
                identify rows.Note that object declaration via XML must occur within
                a definitions element. See for more information.
            </description>
            <name>key</name>
            <foo>blah</foo>
            <fields>
                <field>
                    <description>
                        Hi. This is a test.
                    </description>
                    <name>elements</name>
                    <bar>blah</bar>
                    <factory>
                        <localRenderer>
                            <description>A primitive object and arrays thereof. TEST
                            </description>
                            <name>element</name>
                            <baz>blah</baz>
                            <fields>
                                <field>
                                    <description>Alias by which the element may be referenced.
                                    </description>
                                    <name>name</name>
                                    <bar>blah</bar>
                                </field>
                                <field>
                                    <description>Type of the data; a primitive object of array
                                        thereof.
                                    </description>
                                    <name>type</name>
                                    <bar>blah</bar>
                                </field>
                            </fields>
                        </localRenderer>
                    </factory>
                </field>
            </fields>
        </renderer>
    

1 个答案:

答案 0 :(得分:1)

如果您只想更改文档A中的description节点,那么为了保留顺序,您的XSLT中应该有一个与description节点匹配的模板,而不是其父节点: / p>

 <xsl:template match="description">

然后通过查看前面的兄弟

来获取name
 <xsl:variable name="name" select="preceding-sibling::name[1]" />

然后,要检查文档B中是否存在节点,您可以使用另一个变量

 <xsl:variable name="lookup" select="$desc//node[@name = $name]" />

然后,使用xsl:choose确定是否从A或B复制描述是一个简单的例子

    <xsl:choose>
        <xsl:when test="$lookup">
            <xsl:copy-of select="$lookup/description" />
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="identity" />
        </xsl:otherwise>
    </xsl:choose> 

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:variable name="desc" select="document('doc.out.xml')" />

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

    <xsl:template match="description">
        <xsl:variable name="name" select="preceding-sibling::name[1]" />
        <xsl:variable name="lookup" select="$desc//node[@name = $name]" />
        <xsl:choose>
            <xsl:when test="$lookup">
                <xsl:copy-of select="$lookup/description" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="identity" />
            </xsl:otherwise>
        </xsl:choose> 
    </xsl:template>
</xsl:stylesheet>

请注意,在此处使用xsl:choose而不是在模板匹配条件中的原因是因为在XSLT 1.0中(根据W3C XSLT specification它是一个match属性的值包含VariableReference的错误

这意味着您的XSLT中的模板匹配<xsl:template match="//*[name=$desc//node/@name]">严格来说是一个错误(尽管某些XSLT 1.0处理器可能不遵循规范并允许它,如您的情况)

相关问题