代码之家  ›  专栏  ›  技术社区  ›  bortzmeyer

用XSLT展平:我想将一种元素移动一级

  •  2
  • bortzmeyer  · 技术社区  · 5 年前

    我有一个XML文件,其中元素B在元素a中,我想把它们向上移动。发件人:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
      <A>
        <C>Text</C>
        Text again
    
        More text
        <D>Other text</D>
        <B>Text again</B>
        <C>No</C>
        <D>May be</D>
        <B>What?</B>
      </A>
      <A>
        Something
        <B>Nothing</B>
        <D>Again</D>
        <B>Content</B>
        End
      </A>
    </root>
    

    我想要:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
      <A>
        <C>Text</C>
        Text again
    
        More text
        <D>Other text</D>
      </A>
      <B>Text again</B>
      <A>
        <C>No</C>
        <D>May be</D>
      </A>
      <B>What?</B>
      <A>
        Something
      </A>
      <B>Nothing</B>
      <A>
        <D>Again</D>
      </A>
      <B>Content</B>
      <A>
        End
      </A>
    </root>
    

    我拥有的最接近的XSLT程序是:

    <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 method="xml" indent="yes"/>
    
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="A">
          <xsl:for-each select="*">
        <xsl:choose>
          <xsl:when test="name()='B'">
                <xsl:apply-templates select="."/>
          </xsl:when>
          <xsl:otherwise>
                      <xsl:element name="A">
                          <xsl:apply-templates select="."/>
              </xsl:element>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each>
        </xsl:template>
    
    </xsl:stylesheet>
    

    <A><C>No</C></A>
    <A><D>May be</D></A>
    

    我想要的地方:

    <A><C>No</C>
       <D>May be</D></A>
    

    主要用例是生成HTML,其中UL和OL不能在P中。

    这个问题与…有关,但不完全相同 xslt flattening out child elements in a DocBook para element (也可能是 Flatten xml hierarchy using XSLT )

    3 回复  |  直到 5 年前
        1
  •  2
  •   michael.hor257k    5 年前

    正如我在对你的问题的评论中所说的,这是 节点,并创建新的父节点 A 每个组的元素由 B 元素。

    在XSLT1.0中,可以使用所谓的 同级递归 :

    <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="/root">
        <xsl:copy>
            <xsl:apply-templates select="A"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="A">
        <xsl:copy>
            <xsl:apply-templates select="node()[1][not(self::B)]" mode="sibling"/>
        </xsl:copy>
        <xsl:apply-templates select="B[1]" mode="sibling"/>
    </xsl:template>
    
    <xsl:template match="node()" mode="sibling">
        <xsl:copy-of select="." />
        <xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
    </xsl:template>
    
    <xsl:template match="B" mode="sibling">
        <xsl:copy-of select="." />
        <xsl:if test="following-sibling::node()[normalize-space()]">
            <A>
                <xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
            </A>
            <xsl:apply-templates select="following-sibling::B[1]" mode="sibling"/>
        </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    
        2
  •  1
  •   zx485    5 年前

    XSLT-1.0解决方案是非常难看的,如下所示。输出符合要求,但仅适用于这个简单的MCVE。一般的解决办法比@迈克尔·霍尔257K在评论中提到。如果没有更多的数据,就不可能在XSLT-1.0中创建更好的解决方案。XSLT-2.0及更高版本的解决方案可以简化这一点。

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:template match="/root">
            <xsl:copy>
                <xsl:for-each select="A">
                    <xsl:if test="normalize-space(text()[1])">
                        <A>
                            <xsl:copy-of select="text()[1]" />
                        </A>
                    </xsl:if>
                    <xsl:if test="preceding::*">
                        <xsl:copy-of select="B[1]" />
                    </xsl:if>
                    <A>
                        <xsl:copy-of select="C[1] | C[1]/following-sibling::text()[1] | D[1]" />
                    </A>
                    <xsl:if test="not(preceding::*)">
                        <xsl:copy-of select="B[1]" />
                    </xsl:if>
                    <A>
                        <xsl:copy-of select="C[2] | C[2]/following-sibling::text()[1]" />
                        <xsl:if test="D[2]">
                            <xsl:copy-of select="D[2]" />
                        </xsl:if>
                    </A>
                    <xsl:copy-of select="B[2]" />
                    <xsl:if test="normalize-space(text()[last()])">
                        <A>
                            <xsl:copy-of select="text()[last()]" />
                        </A>
                    </xsl:if>
                </xsl:for-each>
            </xsl:copy>
        </xsl:template>         
    
    </xsl:stylesheet>
    

    <A><C>No</C></A>
    <A><D>May be</D></A>
    

    在上面的代码中对其进行了适当的处理。所以它的输出是

    <A>
        <C>No</C>
        <D>May be</D>
    </A>
    
        3
  •  0
  •   Martin Honnen    5 年前

    group-adjacent=". instance of element(B)" group-adjacent="boolean(self::B)" ,这里是一个XSLT3示例(XSLT3由Java和.NET上的Saxon9.8或9.9支持( https://sourceforge.net/projects/saxon/files/Saxon-HE/ )由Altova自2017年发布):

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
    
      <xsl:template match="A">
          <xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
              <xsl:choose>
                  <xsl:when test="current-grouping-key()">
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:when>
                  <xsl:otherwise>
                      <xsl:copy select="..">
                          <xsl:apply-templates select="current-group()"/>
                      </xsl:copy>
                  </xsl:otherwise>
              </xsl:choose>
          </xsl:for-each-group>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/gWmuiKv

    在XSLT2中,您需要详细说明 <xsl:mode on-no-match="shallow-copy"/> 作为身份转换模板并使用 xsl:element xsl:copy

    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
    
      <xsl:template match="A">
          <xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
              <xsl:choose>
                  <xsl:when test="current-grouping-key()">
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:when>
                  <xsl:otherwise>
                      <xsl:element name="{name(..)}" namespace="{namespace-uri(..)}">
                          <xsl:apply-templates select="current-group()"/>
                      </xsl:element>
                  </xsl:otherwise>
              </xsl:choose>
          </xsl:for-each-group>
      </xsl:template>
    
    </xsl:transform>
    

    http://xsltransform.hikmatu.com/pPqsHT2