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



>Background

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

我的问题与此类似。但是,我对建议的答案遇到的一个限制是节点顺序不一定保留。如果可能的话,我希望保留原始节点顺序。我当前的 XSL 仅执行以下操作:

  1. 匹配要合并的描述节点,然后将它们复制到其他节点之前。
  2. 然后复制其他所有内容(所有不是描述的内容)。

我的 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>

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

 <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 规范)中,匹配属性的值包含变量引用是一个错误

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

最新更新