使用 XSLT 将一个元素的值添加到另一个元素

Use XSLT to add value from one element to another

我有一个源 XML 文件如下:

<section name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="" xmlns="http://composition.bowne.com/2010/v4">
  <table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
    <colspec colnum="1" colname="1" />
    <colspec colnum="2" colname="2" />
    <tbody>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
        <td colname="1">
          <datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment &amp; Supplies" display="always">Health Care Equipment &amp; Supplies</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
        </td>
      </tr>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
        <td colname="1">
          <datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.5" display="always">[~PONT]1.5[#~PONT]</datapoint>     (Line A)
        </td>
      </tr>
      <tr type="otherassets" level="1" itemtype="otherassets" hierarchykey="858">
        <td colname="1">
          <datapoint type="Literal" subtype="Custom" name="Other Assets" value="Other Assets" display="always">Other Assets</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="0.1" display="always">[~PONT]0.1[#~PONT]</datapoint>     (Line B)
        </td>
      </tr>
    </tbody>
  </table>
</section>

我想做的是从(B 行)的 [~PONT] 和 [#~PONT] 标签之间取 0.1,并将其添加到 [~PONT] 和 [#~PONT] 之间的 1.5 ] 标记(A 行),然后抑制包含(B 行)的节点。结果 XML 应如下所示:

<section name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="" xmlns="http://composition.bowne.com/2010/v4">
  <table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
    <colspec colnum="1" colname="1" />
    <colspec colnum="2" colname="2" />
    <tbody>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
        <td colname="1">
          <datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment &amp; Supplies" display="always">Health Care Equipment &amp; Supplies</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
        </td>
      </tr>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
        <td colname="1">
          <datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.6" display="always">[~PONT]1.6[#~PONT]</datapoint>
        </td>
      </tr>
    </tbody>
  </table>
</section>

我知道我可以使用类似的东西来隔离数值:

<xsl:variable name="Value1" select="substring-before(substring-after(string(.),'[~PONT]'),'[#~PONT]')"/>

不幸的是,这就是我所知道的全部。如果这个问题看起来很模糊,我很抱歉,有点难以解释,所以请向我询问更多细节。在此先感谢您的任何建议。顺便说一句,我正在使用 XSLT 版本 1。

  1. 从身份转换开始,默认情况下所有节点都是 复制到输出。
  2. 然后添加覆盖以抑制您不再需要的 tr。在 在下面的示例中,我关闭了 @itemtype='otherassets'.
  3. 然后为 datapoint 添加覆盖以接收计数。在 在下面的示例中,我关闭了 categorykeyitemtypecolname。您可能希望 adapt/generalize 根据您的完整示例, 但此标准适用于您的示例输入,应该会给您一个 感觉需要什么。

此 XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:b="http://composition.bowne.com/2010/v4">
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="b:tr[@categorykey='623']/b:td[@colname='2']/b:datapoint">
    <xsl:variable name="v1"
                  select="substring-before(substring-after(string(.),'[~PONT]'),'[#~PONT]')"/>
    <xsl:variable name="otherdp"
                  select="../../../b:tr[@itemtype='otherassets']/b:td[@colname='2']/b:datapoint"/>
    <xsl:variable name="v2"
                  select="substring-before(substring-after(string($otherdp),'[~PONT]'),'[#~PONT]')"/>
    <xsl:copy>[~PONT]<xsl:value-of select="$v1 + $v2"/>[#~PONT]</xsl:copy>
  </xsl:template>

  <xsl:template match="b:tr[@itemtype='otherassets']"/>

</xsl:stylesheet>

产生所需的XML输出:

<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://composition.bowne.com/2010/v4" name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="">
  <table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
    <colspec colnum="1" colname="1"/>
    <colspec colnum="2" colname="2"/>
    <tbody>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
        <td colname="1">
          <datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment &amp; Supplies" display="always">Health Care Equipment &amp; Supplies</datapoint>
        </td>
        <td colname="2">
          <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
        </td>
      </tr>
      <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
        <td colname="1">
          <datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
        </td>
        <td colname="2">
          <datapoint>[~PONT]1.6[#~PONT]</datapoint>
        </td>
      </tr>

    </tbody>
  </table>
</section>

我不确定这是否符合要求,因为它们有点不明确,但遵循 XSLT

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:tab="http://composition.bowne.com/2010/v4" version="1.0">
 <xsl:output method="html" doctype-public="XSLT-compat" 
  omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
 <xsl:strip-space elements="*"/>
  <xsl:template match="/">
    <xsl:apply-templates/> 
  </xsl:template>
  <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="tab:datapoint[parent::*/preceding-sibling::*/tab:datapoint[@value='All Others*']]/@value">
    <xsl:attribute name="value">
      <xsl:value-of select="sum(.) + 
          sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
      </xsl:attribute>  
  </xsl:template>
  <xsl:template match="tab:datapoint[parent::*/preceding-sibling::*/tab:datapoint[@value='All Others*']]/text()">
    <xsl:text>[~PONT]</xsl:text>
    <xsl:value-of select="sum(./parent::*/@value) + sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
    <xsl:text>[#~PONT]</xsl:text>
  </xsl:template>
  <xsl:template match="tab:tr[@type='otherassets']"/>
</xsl:transform>

当应用于问题中的输入 XML 时会产生输出

<section xmlns="http://composition.bowne.com/2010/v4" name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="">
 <table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
  <colspec colnum="1" colname="1"></colspec>
  <colspec colnum="2" colname="2"></colspec>
  <tbody>
     <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
        <td colname="1">
           <datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment &amp; Supplies" display="always">Health Care Equipment &amp; Supplies</datapoint>
        </td>
        <td colname="2">
           <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
        </td>
     </tr>
     <tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
        <td colname="1">
           <datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
        </td>
        <td colname="2">
           <datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.6" display="always">[~PONT]1.6[#~PONT]</datapoint>     (Line A)

        </td>
     </tr>
  </tbody>
 </table>

对于此转换,我在 XSLT 中添加了示例名称​​空间,用于输入 XML

的附加名称空间 xmlns="http://composition.bowne.com/2010/v4"
xmlns:tab="http://composition.bowne.com/2010/v4"

模板匹配

<xsl:template match="tab:tr[@type='otherassets']"/>

为空并删除此 tr

模板匹配

<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*
                    /tab:datapoint[@value='All Others*']]/@value">

value 属性的值更改为此值与 tr[@type='otherassets']datapoint 的值之和:

<xsl:attribute name="value">
      <xsl:value-of select="sum(.) + 
          sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
</xsl:attribute> 

要更改文本,模板匹配此trtext()

<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*
                    /tab:datapoint[@value='All Others*']]/text()">

不使用问题中建议的 substring-before()substring-after() ,而是使用数据点的 value 属性的值来获取两个值的总和:

 <xsl:value-of select="sum(./parent::*/@value) + 
       sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>

这是基于 value 属性的值与 [~PONT][#~PONT].

之间文本中的值相同的假设

更新:对于评论中的问题,如果匹配模式中的文本不会被硬编码但参数如何调整:

遵循 XSLT

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tab="http://composition.bowne.com/2010/v4" version="1.0">
<xsl:output method="html" doctype-public="XSLT-compat" 
 omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
 <xsl:strip-space elements="*"/>
  <xsl:param name="otherRemove" select="'otherassets'"/>
  <xsl:param name="otherKeep" select="'All Others*'"/>
  <xsl:template match="/">
    <xsl:apply-templates/> 
  </xsl:template>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/@value">
    <xsl:choose>
      <xsl:when test="parent::tab:datapoint/parent::tab:td
                /preceding-sibling::*/tab:datapoint[@value=$otherKeep]">
        <xsl:attribute name="value">
          <xsl:value-of select="sum(.) + 
           sum(//tab:tr[@type=$otherRemove]/tab:td[2]/tab:datapoint/@value)"/>
        </xsl:attribute> 
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/text()">
    <xsl:choose>
      <xsl:when test="parent::tab:datapoint/parent::tab:td
                /preceding-sibling::*/tab:datapoint[@value=$otherKeep]">
        <xsl:text>[~PONT]</xsl:text>
          <xsl:value-of select="sum(./parent::*/@value) + 
             sum(//tab:tr[@type=$otherRemove]/tab:td[2]/tab:datapoint/@value)"/>
        <xsl:text>[#~PONT]</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="tab:tr">
    <xsl:choose>
       <xsl:when test="@type=$otherRemove"/>
       <xsl:otherwise>
         <xsl:copy>
           <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
       </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:transform>

产生相同的结果。

作为调整两个参数

  <xsl:param name="otherRemove" select="'otherassets'"/>
  <xsl:param name="otherKeep" select="'All Others*'"/>

已添加。
要删除行,现在模板匹配所有 tr

<xsl:template match="tab:tr">
  <xsl:choose>
    <xsl:when test="@type=$otherRemove"/>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

复制所有 tr 如果它们没有 @type 属性值 $otherRemoveotherassets.
匹配 text()@value 的模板同样调整:

<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/text()">

<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/@value">

两个模板都签入 <xsl:choose>

<xsl:when test="parent::tab:datapoint/parent::tab:td
          /preceding-sibling::*/tab:datapoint[@value=$otherKeep]">

对于与第一个版本的硬编码模板匹配模式相同的条件 $otherKeepAll Others*,调整匹配元素的值并复制所有其他 text()@value个元素。