>Background
我想通过将新内容合并到现有文件中来更新 XML 文件。现有文件 (A) 中的所有内容都应保持不变,但要合并的内容(文件 B)除外,在本例中为"描述"节点。
我的问题与此类似。但是,我对建议的答案遇到的一个限制是节点顺序不一定保留。如果可能的话,我希望保留原始节点顺序。我当前的 XSL 仅执行以下操作:
- 匹配要合并的描述节点,然后将它们复制到其他节点之前。
- 然后复制其他所有内容(所有不是描述的内容)。
我的 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 处理器可能不遵循规范并允许它,就像您的情况一样)