使用 XSLT 多次更新同一节点

Update same node multiple times with XSLT

我正在尝试使用 XSLT 更新我拥有的 XML 文档。它需要对文档进行多次更改才能将其转换为其他一些规范。

我想做的是多次更新同一个节点。另一种看待它的方式是我想在文档上进行多次传递,并且每次传递都可以更新同一个节点。

我所有的谷歌搜索都提示使用 xsl:template 的 'mode' 属性,但是 'mode' 属性的所有示例都涉及 输出 节点两次 - 就像在内容 table 中使用标题并再次将其用作章节标题的情况一样。我不想两次输出节点,我想更新两次。

我的示例 XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version = '2.0'
     xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

  <!-- identity transform (copy over the document) -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <!-- wrap all BBB nodes in parenthesis -->
  <xsl:template match="BBB" >
    <xsl:copy>
        (<xsl:apply-templates select="@*|node()" />)
    </xsl:copy>
  </xsl:template>

  <!-- any BBB that has a CCC should get an attribute indicating so -->
  <xsl:template match="BBB[CCC]">
    <xsl:copy>
        <xsl:attribute name="hasC">true</xsl:attribute>
        <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>


</xsl:stylesheet> 

我的样本xml文档:

<?xml version="1.0" encoding="UTF-8"?>
<source>

<AAA>
     <BBB>Bee bee bee</BBB>
     <BBB>Bee two bee two bee two</BBB>
</AAA>
<AAA>
     <BBB/>
     <CCC>
          <DDD/>
     </CCC>
     <BBB>
          <CCC/>
     </BBB>
</AAA>

</source> 

当前输出:

<?xml version="1.0" encoding="UTF-8"?><source>

<AAA>
     <BBB>
        (Bee bee bee)
    </BBB>
     <BBB>
        (Bee two bee two bee two)
    </BBB>
</AAA>
<AAA>
     <BBB>
        ()
    </BBB>
     <CCC>
          <DDD/>
     </CCC>
     <BBB hasC="true">
          <CCC/>
     </BBB>
</AAA>

</source>

如您所见,具有 'CCC' 的 BBB 节点只更新了一次,使用第二个模板 - 第一个模板没有修改它。理想情况下,我希望输出为

<BBB hasC="true">
  (<CCC/>)
</BBB>

这可能吗?我知道我可以通过将两个模板组合在一起来完成此操作,但我真的希望将它们分开以提高可读性和维护性 - 这两个模板来自不同的需求,将它们分开是理想的。

我想我可以有两个 XSLT 文件并通过两次调用 XSLT 处理器将它们链接在一起,但我希望我不需要那样做。

编辑: 根据我收到的大量评论,我认为我误解了有关 XSLT 和转换的一些基本知识。

我遇到的问题是:我有一堆 XML 符合规范的文档。此规范已由另一组人员更新,因此我需要创建一个 batch/scriptable 进程来更新大量这些 XML 文档,以便它们符合此新规范。

其中一些新更新如下所示。

1) <string lang="FR">hello</string> 需要改为 <string lang="fra">hello</string>

2) <a><b><string>Color</string><b></a> 需要更改为 <a><b><string>Colour</string></b></a> ,但不要触摸 <a><c><string>Color</string></c></a>

3) 所有<orgName><string>***anytext***</string></orgName>需要改为<orgName><string>My Company: ***anytext***</string></orgName>

4) 所有<orgName><string></string></orgName>节点需要改为<organisationName><string></string></orgisationName>

等等。有很多特定的更新可以多次应用于相同的节点,并且有足够多的这些更新使得对文件进行多次传递似乎比创建一个合并所有这些更新的 catch-all 模板更好合并到一个大模板中。

我应该使用 XSLT 来尝试这样做吗?在这种情况下,我需要将这些更改分解为多个文件并将它们链接到脚本中吗?我可以在一个 .xslt 文件中完成所有这些更新吗?

正如更新示例输入的示例:遵循 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" doctype-public="XSLT-compat" 
      encoding="UTF-8" indent="yes" />
 <xsl:strip-space elements="*" />
  <!-- identity transform (copy over the document) -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>
  <!-- wrap all BBB nodes in parenthesis -->
  <xsl:template match="BBB">
    <xsl:copy>
       <xsl:if test="CCC">
          <xsl:attribute name="hasC">true</xsl:attribute>
       </xsl:if>
       (<xsl:apply-templates select="@*|node()" />)
    </xsl:copy>
  </xsl:template>
  <xsl:template match="orgName[string]">
    <organisationName>
      <xsl:apply-templates />
    </organisationName>
  </xsl:template>
  <xsl:template match="a/b/string[text()='Color']">
    <xsl:text>Colour</xsl:text>
  </xsl:template>
  <xsl:template match="orgName/string/text()">
    <xsl:text>Company:</xsl:text>
    <xsl:value-of select="." />
  </xsl:template>
  <xsl:template match="string[@lang='FR']">
    <xsl:copy>
      <xsl:attribute name="lang">fra</xsl:attribute>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

应用于示例输入时 XML

<?xml version="1.0" encoding="UTF-8"?>
<source>
<AAA>
  <BBB>Bee bee bee</BBB>
  <BBB>Bee two bee two bee two</BBB>
    <test>
      <string lang="FR">hello</string>
    </test>
    <a>
       <b>
         <string>Color</string>
       </b>
    </a>
    <a>
       <c>
         <string>Color</string>
       </c>
    </a>
    <orgName>
       <string>***anytext***</string>
    </orgName>
    <orgName>
       <notstring>***anytext***</notstring>
    </orgName>
  </AAA>
  <AAA>
      <BBB />
      <CCC>
        <DDD />
      </CCC>
    <BBB>
      <CCC />
    </BBB>
  </AAA>
</source>

产生以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<source>
  <AAA>
    <BBB>(Bee bee bee)</BBB>
    <BBB>(Bee two bee two bee two)</BBB>
    <test>
      <string lang="fra">hello</string>
    </test>
    <a>
      <b>Colour</b>
    </a>
    <a>
      <c>
        <string>Color</string>
      </c>
    </a>
    <organisationName>
      <string>Company:***anytext***</string>
    </organisationName>
    <orgName>
     <notstring>***anytext***</notstring>
  </orgName>
  </AAA>
  <AAA>
    <BBB>()</BBB>
    <CCC>
      <DDD />
    </CCC>
    <BBB hasC="true">
     (<CCC />)
    </BBB>
  </AAA>
</source>

您已经提到更新中添加的 4 个要求只是应应用于输入的许多更改列表的一个示例 XML,因此这只是一个示例,说明各种更改如何可以处理相同的节点 - 例如:

<xsl:template match="orgName[string]">

匹配包含节点 string 的节点 orgName。通过

<organisationName>
  <xsl:apply-templates />
</organisationName>

此模板将 orgName 重命名为 organisationName

第二个模板匹配orgName节点中string节点的text()

<xsl:template match="orgName/string/text()">

并在这段文字前加上Company:

<xsl:text>Company:</xsl:text>
  <xsl:value-of select="." />

两个模板的结果:

<organisationName>
   <string>Company:***anytext***</string>
</organisationName>

示例输入中的组织名称

<orgName><notstring>***anytext***</notstring></orgName>

以及文本保持不变,因为 orgName 不包含 string

我还删除了匹配 BBB[CCC] 的模板以设置 hasC 属性。相反,我添加了

<xsl:if test="CCC">
  <xsl:attribute name="hasC">true</xsl:attribute>
</xsl:if>

到模板匹配 BBB.

我不建议使用 XSLT 来应用您需要的所有更改,因为我不知道必须应用的所有特定更改 - 这实际上取决于所需更改的输入和完整规范。我只是想提供一个示例,说明如何使用单个 XSLT 处理您在问题中提到的更改。