使用XSLT多次更新同一节点

时间:2015-01-09 20:26:33

标签: xml xslt

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

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

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

我的示例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文档。此规范已由另一组人员更新,因此我需要创建一个批处理/脚本化过程,以便能够更新大量这些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>

等等。有许多特定的更新可以多次应用于相同的节点,并且有足够的这些更新,使得多次传递到文件似乎是一个更好的主意,然后创建一个全能模板,将所有这些更新合并为一个大模板。

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

1 个答案:

答案 0 :(得分:3)

就像更新的示例输入的示例:遵循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]">

匹配包含节点orgName的节点string。由

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

此模板将orgName重命名为organisationName

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

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

并在此文字前添加Company:

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

两个模板的结果:

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

示例输入中的orgName

<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处理您在问题中提到的更改。