【问题标题】:Duplicate elements occuring while transforming XML in grouping elements in XSLT 2.0在 XSLT 2.0 中的分组元素中转换 XML 时出现重复元素
【发布时间】:2019-09-09 09:32:27
【问题描述】:

在转换此元素时发生在多个位置<p content-type="paratext">paratext 2_1</p>

XML 输入:

<?xml version="1.0" encoding="UTF-8"?>
<body>
    <p content-type="heading-01">heading-01</p>
    <p content-type="paratext">paratext 1</p>
    <p content-type="paratext">paratext 1</p>
    <p content-type="heading-01">heading-01</p>
    <p content-type="heading-02">heading-02</p>
    <p content-type="paratext">paratext 2_1</p>
    <p content-type="heading-02">heading-02</p>
    <p content-type="paratext">paratext 2_2</p>
    <p content-type="heading-01">heading-01</p>
    <p content-type="paratext">paratext 3</p>
    <p content-type="paratext">paratext 3</p>
    <p content-type="heading-01">heading-01</p>
    <p content-type="paratext">paratext 4</p>
    <p content-type="paratext">paratext 4</p>
</body>

要求的输出:

<?xml version="1.0" encoding="UTF-8"?>
<body>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 1</p>
        <p content-type="paratext">paratext 1</p>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <sec sec-type="heading-02">
            <title>heading-02</title>
            <p content-type="paratext">paratext 2_1</p>
        </sec>
        <sec sec-type="heading-02">
            <title>heading-02</title>
            <p content-type="paratext">paratext 2_2</p>
        </sec>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 3</p>
        <p content-type="paratext">paratext 3</p>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 4</p>
        <p content-type="paratext">paratext 4</p>
    </sec>
</body>

当前输出:

<?xml version="1.0" encoding="UTF-8"?>
<body>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 1</p>
        <p content-type="paratext">paratext 1</p>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <sec sec-type="heading-02">
            <title>heading-02</title>
            <p content-type="paratext">paratext 2_1</p>
        </sec>
        <p content-type="paratext">paratext 2_1</p>
        <sec sec-type="heading-02">
            <title>heading-02</title>
            <p content-type="paratext">paratext 2_2</p>
        </sec>
        <p content-type="paratext">paratext 2_2</p>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 3</p>
        <p content-type="paratext">paratext 3</p>
    </sec>
    <sec sec-type="heading-01">
        <title>heading-01</title>
        <p content-type="paratext">paratext 4</p>
        <p content-type="paratext">paratext 4</p>
    </sec>
</body>

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

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

    <xsl:template match="body">
        <body>
            <xsl:for-each select="p[@content-type = 'heading-01']">
                <sec sec-type="{@content-type}">
                    <title>
                        <xsl:value-of select="."/>
                    </title>
                    <xsl:apply-templates select="following-sibling::node() except (following-sibling::p[@content-type = ('heading-01', 'referencetitle')], following-sibling::p[@content-type = ('heading-01', 'referencetitle')]/following-sibling::node())"/>
                </sec>
            </xsl:for-each>
        </body>
    </xsl:template>

    <xsl:template match="p[@content-type = 'heading-02']">
        <sec sec-type="{@content-type}">
            <title>
                <xsl:value-of select="."/>
            </title>
            <xsl:apply-templates select="following-sibling::node() except (following-sibling::p[@content-type = ('heading-01', 'heading-02', 'referencetitle')], following-sibling::p[@content-type = ('heading-01', 'heading-02', 'referencetitle')]/following-sibling::node())"/>
        </sec>
    </xsl:template>

</xsl:stylesheet>

【问题讨论】:

  • 您可以在当前输出中看到 paratext 2_1paratext 2_2 在第 2 节和第 1 节中也有多个。它应该只包裹在第 2 部分而不是 1

标签: xml xslt xslt-2.0 xslt-grouping


【解决方案1】:

以下是 XSLT 3,但如果您使用 concat 而不是 ||xsl:value-of 而不是文本值模板 {.},则使用 for-each-group 的递归函数也可以在 XSLT 2 中工作:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    expand-text="yes"
    version="3.0">

  <xsl:function name="mf:nest" as="element()*">
      <xsl:param name="input" as="element(p)*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$input" group-starting-with="p[@content-type = 'heading-' || format-number($level, '00')]">
          <xsl:choose>
              <xsl:when test="self::p[@content-type = 'heading-' || format-number($level, '00')]">
                  <sec sec-type="{@content-type}">
                      <title>{.}</title>
                      <xsl:apply-templates select="mf:nest(tail(current-group()), $level + 1)"/>
                  </sec>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:function>

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:template match="body">
      <xsl:copy>
          <xsl:apply-templates select="mf:nest(*, 1)"/>
      </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6rewNxB/1

XSLT 2:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all">

  <xsl:function name="mf:nest" as="element()*">
      <xsl:param name="input" as="element(p)*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$input" group-starting-with="p[@content-type = concat('heading-', format-number($level, '00'))]">
          <xsl:choose>
              <xsl:when test="self::p[@content-type = concat('heading-', format-number($level, '00'))]">
                  <sec sec-type="{@content-type}">
                      <title>
                          <xsl:value-of select="."/>
                      </title>
                      <xsl:apply-templates select="mf:nest(subsequence(current-group(), 2), $level + 1)"/>
                  </sec>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:function>

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

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

  <xsl:template match="body">
      <xsl:copy>
          <xsl:apply-templates select="mf:nest(*, 1)"/>
      </xsl:copy>
  </xsl:template>

</xsl:transform>

http://xsltransform.net/pNEhB3u

【讨论】:

    【解决方案2】:

    怎么样:

    XSLT 2.0

    <xsl:stylesheet version="2.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="/body">
        <xsl:copy>
            <xsl:for-each-group select="p" group-starting-with="p[@content-type='heading-01']">
                <sec sec-type="heading-01">
                    <title>
                        <xsl:value-of select="."/>
                    </title>
                    <xsl:choose>
                        <xsl:when test="current-group()[@content-type='heading-02']">
                            <xsl:for-each-group select="current-group()[position() > 1]" group-starting-with="p[@content-type='heading-02']">
                                <sec sec-type="heading-02">
                                    <title>
                                        <xsl:value-of select="."/>
                                    </title>
                                    <xsl:copy-of select="current-group()[@content-type='paratext']"/>
                                </sec>
                            </xsl:for-each-group>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:copy-of select="current-group()[@content-type='paratext']"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </sec>  
            </xsl:for-each-group>
      </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    演示https://xsltfiddle.liberty-development.net/bFWR5DF

    【讨论】:

      【解决方案3】:

      感谢大家的回复,我用for-each-group通过下面的代码解决了这个问题:

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
      
          <xsl:template match="@* | node()">
              <xsl:copy>
                  <xsl:apply-templates select="@* | node()"/>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template match="body">
              <body>
                  <xsl:for-each-group select="*" group-starting-with="p[@content-type='heading-01']">
                      <xsl:choose>
                          <xsl:when test="@content-type='heading-01'">
                              <sec content-type="{@content-type}">
                                  <xsl:for-each-group select="current-group()" group-starting-with="p[@content-type='heading-02']">
                                      <xsl:choose>
                                          <xsl:when test="@content-type='heading-02'">
                                              <sec content-type="{@content-type}">
                                                  <xsl:for-each-group select="current-group()" group-starting-with="p[@content-type='heading-03']">
                                                      <xsl:choose>
                                                          <xsl:when test="@content-type='heading-03'">
                                                              <sec content-type="{@content-type}">
                                                                  <xsl:apply-templates select="current-group()"/>
                                                              </sec>
                                                          </xsl:when>
                                                          <xsl:otherwise>
                                                              <xsl:apply-templates select="current-group()"/>
                                                          </xsl:otherwise>
                                                      </xsl:choose>
                                                  </xsl:for-each-group>
                                              </sec>
                                          </xsl:when>
                                          <xsl:otherwise>
                                              <xsl:apply-templates select="current-group()"/>
                                          </xsl:otherwise>
                                      </xsl:choose>
                                  </xsl:for-each-group>
                              </sec>
                          </xsl:when>
                          <xsl:otherwise>
                              <xsl:apply-templates select="current-group()"/>
                          </xsl:otherwise>
                      </xsl:choose>
                  </xsl:for-each-group>
              </body>
          </xsl:template>
      
          <xsl:template match="p">
              <xsl:choose>
                  <xsl:when test="starts-with(@content-type, 'heading')">
                      <title>
                          <xsl:apply-templates/>
                      </title>
                  </xsl:when>
                  <xsl:otherwise>
                      <p>
                          <xsl:apply-templates select="@* | node()"/>
                      </p>
                  </xsl:otherwise>
              </xsl:choose>
          </xsl:template>
      
      </xsl:stylesheet>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-13
        • 1970-01-01
        • 2016-02-22
        • 1970-01-01
        • 2014-03-11
        • 2011-04-06
        • 1970-01-01
        相关资源
        最近更新 更多