简体   繁体   English

合并特定元素,但使用XSLT 1.0保持文档顺序

[英]Merge specific elements but keep document order using XSLT 1.0

Given the following document: 给定以下文件:

<doc>
  <a>a</a>
  <b>1</b>
  <b>2</b>
  <b>3</b>
  <c>c</c>
</doc>

I want this to be turned into: 我希望将其转换为:

<doc>
  <a>a</a>
  <b>1,2,3</b>
  <c>c</c>
</doc>

What I have so far is (mostly taken from another post here at SO): 到目前为止,我所拥有的(大部分来自SO上的另一篇文章):

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

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

  <xsl:template match="doc">
    <xsl:copy>
      <xsl:apply-templates select="*[not(self::b)]"/>
      <b><xsl:apply-templates select="b/text()"/></b>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="b/text()">
    <xsl:if test="position() &gt; 1">,</xsl:if>
    <xsl:value-of select="."/>
  </xsl:template>

</xsl:stylesheet>

This creates the following result: 这将产生以下结果:

<doc><a>a</a><c>c</c><b>1,2,3</b></doc>

But I'm struggling to find a solution that keeps the document order intact. 但是,我一直在努力寻找一种可以保持文档顺序完整的解决方案。 Any run of <b> elements should be replaced by a single element containing the text of the original elements as a comma separated list. 任何<b>元素运行都应替换为包含原始元素文本(以逗号分隔的列表)的单个元素。 The other elements should not be reordered. 其他元素不应重新排序。

Why don't you do simply: 为什么不简单地做:

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

<xsl:template match="/doc">
    <xsl:copy>
        <xsl:copy-of select="a"/>
        <b>
            <xsl:for-each select="b">
                <xsl:value-of select="."/>
                <xsl:if test="position() !=last()">,</xsl:if>
            </xsl:for-each>
        </b>
        <xsl:copy-of select="c"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

This is assuming you know the structure of the incoming XML and can enumerate the elements before b and after it separately. 假设您知道传入XML的结构,并且可以分别枚举b之前和之后的元素。 Otherwise you'd have to do something like: 否则,您将必须执行以下操作:

<xsl:template match="/doc">
    <xsl:copy>
        <xsl:apply-templates select="*[not(self::b or preceding-sibling::b)]"/>
        <b>
            <xsl:for-each select="b">
                <xsl:value-of select="."/>
                <xsl:if test="position() !=last()">,</xsl:if>
            </xsl:for-each>
        </b>
        <xsl:apply-templates select="*[not(self::b or following-sibling::b)]"/>
    </xsl:copy>
</xsl:template>

You can use sibling recursion in a different mode: 您可以在其他模式下使用同级递归:

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

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="b[not(preceding-sibling::*[1][self::b])]">
        <xsl:copy>
            <xsl:apply-templates select="." mode="merge"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="b[preceding-sibling::*[1][self::b]]"/>

    <xsl:template match="b" mode="merge">
        <xsl:value-of select="."/>
        <xsl:variable name="next" select="following-sibling::*[1][self::b]"/>
        <xsl:if test="$next">
            <xsl:text>,</xsl:text>
            <xsl:apply-templates select="$next" mode="merge"/>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM