将匹配的元素添加到新元素,同时保持那些不匹配的元素

时间:2014-01-29 10:40:08

标签: xslt xslt-1.0 xslt-2.0

我正在尝试处理XML文件,我知道某些具有特定属性的元素应该是新元素的子元素。那些不匹配的元素的其他兄弟姐妹应该保持不变。一个例子会更好。

我要匹配的代币具有属性id =' t_15'和id =' t_16'。

这是输入XML:

<text>
  <div id="d_1">
    <p id="p_1">
      <s id="s_1">
        <token id="t_13" lemma="colleague" pos="NN">Colleagues</token>
        <token id="t_14" lemma="," pos=",">,</token>
        <token id="t_15" lemma="we" pos="PP">we</token>
        <token id="t_16" lemma="now" pos="RB">now</token>
        <token id="t_17" lemma="come" pos="VVP">come</token>
        <token id="t_18" lemma="to" pos="TO">to</token>
        <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token>
        <token id="t_20" lemma="." pos="SENT">.</token>
      </s>
      <s id="s_2">
        <token id="t_21" lemma="these" pos="DT">These</token>
        <token id="t_22" lemma="be" pos="VBP">are</token>
        <token id="t_23" lemma="the" pos="DT">the</token>
        <token id="t_24" lemma="name" pos="NNS">names</token>
        <token id="t_25" lemma="I" pos="PP">I</token>
        <token id="t_26" lemma="will" pos="MD">will</token>
        <token id="t_27" lemma="call" pos="VV">call</token>
        <token id="t_28" lemma="out" pos="RP">out</token>
        <token id="t_29" lemma="." pos="SENT">.</token>
      </s>
    </p>
  </div>
</text>

这是我想要的输出:

<text>
  <div id="d_1">
    <p id="p_1">
      <s id="s_1">
        <token id="t_13" lemma="colleague" pos="NN">Colleagues</token>
        <token id="t_14" lemma="," pos=",">,</token>
        <e>
          <token id="t_15" lemma="we" pos="PP">we</token>
          <token id="t_16" lemma="now" pos="RB">now</token>
        </e>
        <token id="t_17" lemma="come" pos="VVP">come</token>
        <token id="t_18" lemma="to" pos="TO">to</token>
        <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token>
        <token id="t_20" lemma="." pos="SENT">.</token>
      </s>
      <s id="s_2">
        <token id="t_21" lemma="these" pos="DT">These</token>
        <token id="t_22" lemma="be" pos="VBP">are</token>
        <token id="t_23" lemma="the" pos="DT">the</token>
        <token id="t_24" lemma="name" pos="NNS">names</token>
        <token id="t_25" lemma="I" pos="PP">I</token>
        <token id="t_26" lemma="will" pos="MD">will</token>
        <token id="t_27" lemma="call" pos="VV">call</token>
        <token id="t_28" lemma="out" pos="RP">out</token>
        <token id="t_29" lemma="." pos="SENT">.</token>
      </s>
    </p>
  </div>
</text>

这是我正在使用的样式表:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="1.0">
    <xsl:output omit-xml-declaration="yes" indent="no"></xsl:output>

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

    <xsl:variable name="dif_tok">
        <dif_tok>t_15</dif_tok>
        <dif_tok>t_16</dif_tok>
    </xsl:variable>

    <xsl:template match="s">
        <xsl:copy>
            <xsl:apply-templates select="@*"></xsl:apply-templates>
            <e>
                <xsl:copy-of select="*[@id = $dif_tok/dif_tok]"></xsl:copy-of>
            </e>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

这就是我最终获得的结果:

<text>
  <div id="d_1">
    <p id="p_1">
      <s id="s_1"><e><token id="t_15" lemma="we" pos="PP">we</token><token id="t_16" lemma="now" pos="RB">now</token></e></s>
      <s id="s_2"><e/></s>
    </p>
  </div>
</text>

3 个答案:

答案 0 :(得分:0)

  

具有特定属性的某些元素应该是新元素的子元素

编写单独的模板以匹配您感兴趣的token元素。

<xsl:template match="token[@id ='t_15']">

在模板中,生成e元素,并利用apply-templates的行为将其填充到@id = t_15 and t_16的标记中。然后,让身份变换完成所有其余的工作。

<强>样式表

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>

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

   <xsl:template match="token[@id ='t_15']">
      <e>
         <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
         <xsl:apply-templates select="following-sibling::token[1]"/>
      </e>
   </xsl:template>

</xsl:stylesheet>

<强>输出

<?xml version="1.0" encoding="UTF-8"?>
<text>
  <div id="d_1">
      <p id="p_1">
         <s id="s_1">
            <token id="t_13" lemma="colleague" pos="NN">Colleagues</token>
            <token id="t_14" lemma="," pos=",">,</token>
            <e>
               <token id="t_15" lemma="we" pos="PP">we</token>
               <token id="t_16" lemma="now" pos="RB">now</token>
            </e>
            <token id="t_16" lemma="now" pos="RB">now</token>
            <token id="t_17" lemma="come" pos="VVP">come</token>
            <token id="t_18" lemma="to" pos="TO">to</token>
            <token id="t_19" lemma="catch-the-eye" pos="NN">catch-the-eye</token>
            <token id="t_20" lemma="." pos="SENT">.</token>
         </s>
         <s id="s_2">
            <token id="t_21" lemma="these" pos="DT">These</token>
            <token id="t_22" lemma="be" pos="VBP">are</token>
            <token id="t_23" lemma="the" pos="DT">the</token>
            <token id="t_24" lemma="name" pos="NNS">names</token>
            <token id="t_25" lemma="I" pos="PP">I</token>
            <token id="t_26" lemma="will" pos="MD">will</token>
            <token id="t_27" lemma="call" pos="VV">call</token>
            <token id="t_28" lemma="out" pos="RP">out</token>
            <token id="t_29" lemma="." pos="SENT">.</token>
         </s>
      </p>
  </div>
</text>

您尝试的解决方案使用变量。这也是一个很好的方法:

<xsl:variable name="start" select="'t_15'"/>

<xsl:template match="token[@id=$start]">
  <e>
     <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
     </xsl:copy>
     <xsl:apply-templates select="following-sibling::token[1]"/>
  </e>
</xsl:template>

答案 1 :(得分:0)

我稍微修改了Mathias制作的样式表。见下文。

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>

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

    <xsl:variable name="start" select="'t_15'"/>
    <xsl:variable name="middle" select="'t_16'"/>
    <xsl:variable name="end" select="'t_17'"/>

    <xsl:template match="token[@id =$start]">
        <e>
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
            <xsl:apply-templates select="following-sibling::token[@id=$middle]" mode="group"/>
            <xsl:apply-templates select="following-sibling::token[@id=$end]" mode="group"/>
        </e>
    </xsl:template>

    <xsl:template match="token[@id =$middle]|token[@id =$end]" mode="group">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="token[@id =$middle]|token[@id =$end]"/>

</xsl:stylesheet>

答案 2 :(得分:0)

编辑:因为我写了这个答案,问题已经改为需要XSLT 1.0而不是2.0,但我会在这里留下答案供参考。


由于您使用的是XSLT 2.0,因此这是group-adjacent for-each-group机制的理想情况。假设您拥有以前的身份模板,那么

怎么样
<xsl:variable name="tokensToGroup" select="('t_15', 't_16')" />

<xsl:template match="s">
  <xsl:copy>
    <xsl:apply-templates select="@*" />
    <xsl:for-each-group select="token" group-adjacent="@id = $tokensToGroup">
      <xsl:choose>
        <xsl:when test="current-grouping-key()">
          <e><xsl:apply-templates select="current-group()" /></e>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

其ID与$tokensToGroup匹配的相邻令牌将被收集到current-grouping-key()值为布尔true的单个组中,其他令牌将被收集到密钥为{{1}的组中我们只使用false键在<e>...</e>周围包裹true

如果不是要分组的所有ID的列表,而是只有第一个和最后一个令牌的ID,那么您可以使用相同的机制,但使用不同的分组键定义

<xsl:param name="first" select="'t_15'" />
<xsl:param name="last" select="'t_17'" />

<xsl:for-each-group select="token"
   group-adjacent="( (. | preceding-sibling::token)[@id = $first] and
                     (. | following-sibling::token)[@id = $last] )">

此处true键值适用于在$firstToken之前或之前$lastToken 之后的元素。假设处理器尚未对其进行优化,例如

,使用密钥可能会更有效
<xsl:key name="tokenById" match="token" use="@id" />

<xsl:variable name="firstToken" select="key('tokenById', $first)" />
<xsl:variable name="lastToken" select="key('tokenById', $last)" />

<xsl:for-each-group select="token"
   group-adjacent="( (. is $firstToken or . &gt;&gt; $firstToken) and
                     (. is $lastToken or . &lt;&lt; $lastToken) )">