xslt将具有相同属性的相邻兄弟合并为一个,同时连接它们的文本

Posted

技术标签:

【中文标题】xslt将具有相同属性的相邻兄弟合并为一个,同时连接它们的文本【英文标题】:xslt collapsing neighbouring siblings with identical attribute into one while concatenating their text 【发布时间】:2015-03-20 16:38:36 【问题描述】:

背景:以下是 MSWord 表单的 xslt 修改的 xml 提取。 MSWord 表单中的一些文本不知何故被拆分为多个元素,需要重新组合成一个元素。下面是倒数第二个 XML 输入的实际 sn-p

<Section coord="2.13" posn="2" of="13">
    <Segment coord="1.25" rowno="1" of="25">
        <Entry coord="1.1" colno="1" of="1" desgn="Table">QUAL</Entry>
        <Entry coord="1.1" colno="1" of="1" desgn="Table">I</Entry>
        <Entry coord="1.1" colno="1" of="1" desgn="Table">FICATIONS</Entry>
    </Segment>
    <Segment coord="2.25" rowno="2" of="25">
        <Entry coord="1.1" colno="1" of="1" desgn="Table">ACADEMIC QUALIFICATIONS</Entry>
        <Entry coord="1.1" colno="1" of="1" desgn="Table"> (Most recent first)</Entry>
    </Segment>
    <Segment coord="3.25" rowno="3" of="25">
        <Entry coord="1.4" colno="1" of="4" desgn="Column">Degree/Diploma/Certificate</Entry>
        <Entry coord="2.4" colno="2" of="4" desgn="Column">Institution</Entry>
        <Entry coord="3.4" colno="3" of="4" desgn="Column">Date Conferred</Entry>
        <Entry coord="3.4" colno="3" of="4" desgn="Column">(mm/yyyy)</Entry>
        <Entry coord="4.4" colno="4" of="4" desgn="Column">SAQA Evaluated?</Entry>
        <Entry coord="4.4" colno="4" of="4" desgn="Column">(If not SA qualification)</Entry>
    </Segment>
    <Segment coord="4.25" rowno="4" of="25"/>
    <!-- remaining 21 Segments from Section deleted ... -->
</Section>

注意:@coord 属性是从同级“position().last()”构造的。

所需的合并输出: 以@coord 1.25的Segment为例,三个Entries需要折叠成一个Entry:

&lt;Entry coord="1.1" colno="1" of="1" desgn="Table"&gt;QUALIFICATIONS&lt;/Entry&gt;

他们的文本被连接成一个。

同样,Segment 1.26 有两个条目应该折叠成:

<Entry coord="1.1" colno="1" of="1" desgn="Table">ACADEMIC QUALIFICATIONS (Most recent first)</Entry>

这同样适用于第 3.25 节中的最后两个,具有不同的合并条目:

<Entry coord="3.4" colno="3" of="4" desgn="Column">Date Conferred(mm/yyyy)</Entry>

<Entry coord="4.4" colno="4" of="4" desgn="Column">SAQA Evaluated?(If not SA qualification)</Entry>

我能够(按文档顺序)测试@coord 的重复,例如: test="@coord = following-sibling::Entry/@coord" 开始连接 要么 test="@coord != previous-sibling::Entry/@coord" 停止连接 但我的困难在于在连接文本时推迟 xsl:copy。它在文档顺序上变得混乱(我的失败和未完成的尝试只进行一次连接而不是根据需要进行多次连接):

  <xsl:template match="Segment">
      <xsl:for-each select="Entry" >
          <xsl:choose>
            <xsl:when test="position()= 1 and (@coord = following-sibling::Entry/@coord)" >
              <xsl:copy>
                  <xsl:value-of select="@*"/><xsl:value-of select="text()" /> <xsl:value-of select="following-sibling::Entry/text()" />
              </xsl:copy>
            </xsl:when>
            <xsl:when test="@coord != preceding-sibling::Entry/@coord" >
              <xsl:copy>
                  <xsl:value-of select="@*"/><xsl:value-of select="text()" />
              </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
                <xsl:for-each select=".">
                   <xsl:if test="@coord = following-sibling::Entry/@coord" >    
                       <xsl:value-of select="following-sibling::Entry/text()" />
                  </xsl:if>          
                </xsl:for-each>
            </xsl:otherwise>
          </xsl:choose>
          <xsl:copy>
              <xsl:apply-templates select="node()|@*"/>
          </xsl:copy>
      </xsl:for-each>
  </xsl:template>

似乎以相反的文档顺序连接可能更自然,但仔细考虑仍然很混乱。解决这个问题的最佳方法是什么?

根据我对答案 2 的评论,如何按照建议 () 扩展答案以进行额外的父处理。需要填充并显示在下面的 xml 输入为空属性。

<Section coord="2.13" posn="2" of="13">
<Segment coord="1.25" rowno="1" of="25" frags="" size="">
<Entry coord="1.1" colno="1" of="1" desgn="Table" size="4">QUAL</Entry>
<Entry coord="1.1" colno="1" of="1" desgn="Table" size="1">I</Entry>
<Entry coord="1.1" colno="1" of="1" desgn="Table" size="9">FICATIONS</Entry>
</Segment>
<Segment coord="2.25" rowno="2" of="25" frags="" size="">
<Entry coord="1.1" colno="1" of="1" desgn="Table" size="23">ACADEMIC QUALIFICATIONS</Entry>
<Entry coord="1.1" colno="1" of="1" desgn="Table" size="20"> (Most recent first)</Entry>
</Segment>
<Segment coord="3.25" rowno="3" of="25" frags="" size="">
<Entry coord="1.4" colno="1" of="4" desgn="Column" size="26">Degree/Diploma/Certificate</Entry>
<Entry coord="2.4" colno="2" of="4" desgn="Column" size="11">Institution</Entry>
<Entry coord="3.4" colno="3" of="4" desgn="Column" size="14">Date Conferred</Entry>
<Entry coord="3.4" colno="3" of="4" desgn="Column" size="9">(mm/yyyy)</Entry>
<Entry coord="4.4" colno="4" of="4" desgn="Column" size="15">SAQA Evaluated?</Entry>
<Entry coord="4.4" colno="4" of="4" desgn="Column" size="25">(If not SA qualification)</Entry>
</Segment>
<!-- delete -->
</Section>

对父(段)元素进行额外处理的预期输出:

<!-- deleted prior input xml -->
<Segment coord="1.25" rowno="1" of="25" frags="3" size="14">
<!-- deleted collapsed Entries as transformed -->
</Segment>
<Segment coord="2.25" rowno="2" of="25" frags="2" size="43">
<!-- deleted collapsed Entries as transformed -->
</Segment>
<Segment coord="3.25" rowno="3" of="25" frags="6" size="100">
<!-- deleted collapsed Entries as transformed -->
</Segment>
<!-- deleted rest of input xml -->

【问题讨论】:

您使用的是哪个版本的 XSLT? 1.0 还是 2.0?请添加您的预期输出。 【参考方案1】:

试试这个 XSLT1.0 样式表(这个 XSLT2.0 版本会简单得多):

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform template -->
<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Segment">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:for-each select="Entry[not(@coord = preceding-sibling::Entry/@coord)]">
            <xsl:variable name="Coord" select="@coord"/>
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:for-each select="../Entry[@coord = $Coord]">
                    <xsl:value-of select="."/>
                </xsl:for-each>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
</xsl:transform>

【讨论】:

【参考方案2】:

适用于 xslt1 和 2,但前提是满足以下条件:如果父项和坐标相同,则应折叠两个条目。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>

    <xsl:key name="entry-by-coord" match="Entry" use="concat(generate-id(parent::*), '||', @coord)"/>

    <xsl:template match="/">
        <xsl:apply-templates select="*"/>
    </xsl:template>

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

    <xsl:template match="Entry">
        <xsl:if test="generate-id()=generate-id(key('entry-by-coord', concat(generate-id(parent::*), '||', @coord))[1])">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:copy-of select="key('entry-by-coord', concat(generate-id(parent::*), '||', @coord))/text()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

【讨论】:

上述两种方法都适用于我的 xml 输入,事实上,不仅适用于 sn-p,还适用于整个输入。他们的比较提供了一些关于 XSLT 特性集和与之相关的设计模式的见解。方法 2 显然更可重用,因为它较少依赖元素层次结构文字。谢谢大家。 (是的,我正在寻找这些响应支持的 Xslt1 解决方案。) 第一种方法依赖于 Entry 元素的顺序,即它适用于 1,1,2,2,3 但不适用于 2,1,2,3,1 确实很有趣。如果我可以让您进一步探索这种模式或方法的更一般用途,如果我想在连接之外进行更多处理,如何扩展这个答案?假设我们想为每个文本片段的字符串长度设置一个属性,但也使用它们的总和在父级上做同样的事情,并且还说子测试片段的数量作为父属性。我将修改后的 XML 输入发布到已编辑的原始问题中。 各位,请撤回以上进一步的探索查询。这不是一个格式良好的查询。因为折叠变换使输入片段计数无效。还发现答案 2 属于 Muenchian 分组模式,有据可查,即使在我 2001 年折耳的 Kay 的第二版中也是如此。 XSLT Ref,因此无需响应。版主请退,谢谢

以上是关于xslt将具有相同属性的相邻兄弟合并为一个,同时连接它们的文本的主要内容,如果未能解决你的问题,请参考以下文章

使用纯 CSS 使悬停效果应用于具有相同属性的相邻兄弟姐妹

使用 xslt 2.0 合并具有相同父属性值的元素的 xml 内容

XSLT 将具有父节点的节点移动到具有给定属性的每个父节点的兄弟节点中

如何合并具有“相同父亲”、相同方法和相同 id=0 的两个节点(使用 XSLT)?

使用 XSLT 将具有相同 ID 的元素 (XML) 合并到 txt 文件

XSLT 分组/合并子项(使用密钥)