使用XSL用新节点替换XML节点

时间:2010-06-01 19:16:14

标签: xml xslt xpath

我需要一个XSL解决方案来用新节点替换XML节点。

假设我有以下现有的XML结构:

<root>
    <criteria>
        <criterion>AAA</criterion>
    </criteria>
</root>

我想用以下内容替换一个标准节点:

<criterion>BBB</criterion>
<criterion>CCC</criterion>
<criterion>DDD</criterion>

这样最终的XML结果是:

<root>
    <criteria>
        <criterion>BBB</criterion>
        <criterion>CCC</criterion>
        <criterion>DDD</criterion>
    </criteria>
</root>

我已经尝试过使用substring-before和substring-after来复制结构的前半部分,然后只复制下半部分(为了填补两半之间的新节点),但似乎是substring函数只识别节点标签之间的文本,而不是我想要的标签本身。 :( :(

还有其他解决方案吗?

3 个答案:

答案 0 :(得分:23)

XSL无法替换任何内容。您可以做的最好的事情是复制要保留的部分,然后输出您想要更改的部分,而不是您不想保留的部分。


示例:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

    <!-- This is an identity template - it copies everything
         that doesn't match another template -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

  <!-- This is the "other template". It says to use your BBB-DDD elements
       instead of the AAA element -->
  <xsl:template match="criterion[.='AAA']">
    <xsl:element name="criterion">
      <xsl:text>BBB</xsl:text>
    </xsl:element>
    <xsl:element name="criterion">
      <xsl:text>CCC</xsl:text>
    </xsl:element>
    <xsl:element name="criterion">
      <xsl:text>DDD</xsl:text>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

模板匹配@* | node()匹配任何属性或任何其他类型的节点。诀窍是模板匹配具有优先级。您可以将规则视为“更具体的匹配胜利”。 任何将比“任何属性或其他节点”更具体。这使得“身份”匹配的优先级非常低。

匹配时,它只是复制它在匹配的属性或节点中找到的任何节点。

您拥有的任何其他模板都将具有更高的优先级。无论它们匹配什么,它都是更有效的模板中的代码。例如,如果您只是删除了criterion[.='AAA']模板中的所有内容,则会发现您已完全复制了输入,但“AAA”元素除外。

答案 1 :(得分:6)

这是一个正确的解决方案,可能是最短的之一:

 <xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="criterion[. = 'AAA']">
  <criterion>BBB</criterion>
  <criterion>CCC</criterion>
  <criterion>DDD</criterion> </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换后,生成所需结果

<root>
    <criteria>
        <criterion>BBB</criterion>
        <criterion>CCC</criterion>
        <criterion>DDD</criterion>
    </criteria>
</root>

请注意

  1. 使用身份模板

  2. 特定模板如何覆盖标识模板 - 仅适用于criterion元素,其字符串值为'AAA'

答案 2 :(得分:0)

根据超过一对一皮肤的一般规则

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:template match="/">
        <xsl:apply-templates />
    </xsl:template>
    <!--  
        when you capture a node with the text 'AAA'
            emit the BBB, CCC, DDD nodes
     -->
    <xsl:template match="criterion[text() = 'AAA']">
        <xsl:element name="criterion">
            <xsl:text>BBB</xsl:text>
        </xsl:element>
        <xsl:element name="criterion">
            <xsl:text>CCC</xsl:text>
        </xsl:element>
        <xsl:element name="criterion">
            <xsl:text>DDD</xsl:text>
        </xsl:element>
    </xsl:template>
    <!--  identity template  -->
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>