如何获取具有相同名称的元素并根据 XSLT 中的子节点值对它们进行排序

Posted

技术标签:

【中文标题】如何获取具有相同名称的元素并根据 XSLT 中的子节点值对它们进行排序【英文标题】:How to fetch elements with same name and order them based on a subnode value in XSLT 【发布时间】:2021-03-14 07:39:43 【问题描述】:

我正在尝试转换一个输入 XML,其中所有父节点都说“x”需要被提取、排序,然后根据它们的子节点说“y”值在最终输出 XML 中重新排序。下面是我的 XML,我正在尝试对其进行修改,以便最终的 XML 将元素按升序排列,其中排序是基于 xpath notes/notesBODY/group/text 中的数值完成的,基本上是元素 <text>1. </text> 存在于所有 @987654323 @元素。但是输出 XML 并不像预期的那样,因为我似乎无法弄清楚如何在 XSLT 中准确地使用排序。任何帮助表示赞赏。

输入 XML:

 <root>
    <group>
       <text>group1</text>
    </group>
    <group>
       <text>group2</text>
    </group>
    <notes>
        <notesText>notes1</notesText>
        <notesBODY>
            <group>
                <text>2. </text>
                <text>notesbody1</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
        <notesText>notes2</notesText>
        <notesBODY>
            <group>
                <text>1. </text>
                <text>notesbody2</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
        <notesText>notes3</notesText>
        <notesBODY>
            <group>
                <text>3. </text>
                <text>notesbody3</text>
            </group>
        </notesBODY>
    </notes>
    <group>
        <text>group3</text>
        <notes>
            <notesText>notes4</notesText>
            <notesBODY>
                <group>
                    <text>4. </text>
                    <text>notesbody4</text>
                </group>
            </notesBODY>
        </notes>
    </group>
    <group>
       <text>group4</text>
    </group>
    <group>
       <text>group5</text>
       <notes>
            <notesText>notes5</notesText>
            <notesBODY>
                <group>
                    <text>6. </text>
                    <text>notesbody5</text>
                </group>
            </notesBODY>
        </notes>
    </group>
    <notes>
        <notesText>notes6</notesText>
        <notesBODY>
            <group>
                <text>5. </text>
                <text>notesbody6</text>
            </group>
        </notesBODY>
    </notes>
    <group>
       <text>group6</text>
    </group>
    <group>
       <text>group7</text>
    </group>
    <group>
        <text>group8</text>
        <notes>
            <notesText>notes7</notesText>
            <notesBODY>
                <group>
                    <text>8. </text>
                    <text>notesbody7</text>
                </group>
            </notesBODY>
        </notes>
    </group>
    <notes>
        <notesText>notes8</notesText>
        <notesBODY>
            <group>
                <text>7. </text>
                <text>notesbody8</text>
            </group>
        </notesBODY>
    </notes>
    <group>
       <text>group9</text>
    </group>
 </root>

预期输出:

 <root>
    <group>
       <text>group1</text>
    </group>
    <group>
       <text>group2</text>
    </group>
    <group>
        <text>group3</text>
    </group>
    <group>
       <text>group4</text>
    </group>
    <group>
       <text>group5</text>
    </group>
    <group>
       <text>group6</text>
    </group>
    <group>
       <text>group7</text>
    </group>
    <group>
        <text>group8</text>
    </group>
    <group>
       <text>group9</text>
    </group>
    <notes>
        <notesText>notes2</notesText>
        <notesBODY>
            <group>
                <text>1. </text>
                <text>notesbody2</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
        <notesText>notes1</notesText>
        <notesBODY>
            <group>
                <text>2. </text>
                <text>notesbody1</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
        <notesText>notes3</notesText>
        <notesBODY>
            <group>
                <text>3. </text>
                <text>notesbody3</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
            <notesText>notes4</notesText>
            <notesBODY>
                <group>
                    <text>4. </text>
                    <text>notesbody4</text>
                </group>
            </notesBODY>
        </notes>
    <notes>
        <notesText>notes6</notesText>
        <notesBODY>
            <group>
                <text>5. </text>
                <text>notesbody6</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
            <notesText>notes5</notesText>
            <notesBODY>
                <group>
                    <text>6. </text>
                    <text>notesbody5</text>
                </group>
            </notesBODY>
    </notes>
    <notes>
        <notesText>notes8</notesText>
        <notesBODY>
            <group>
                <text>7. </text>
                <text>notesbody8</text>
            </group>
        </notesBODY>
    </notes>
    <notes>
        <notesText>notes7</notesText>
        <notesBODY>
            <group>
                <text>8. </text>
                <text>notesbody7</text>
            </group>
        </notesBODY>
    </notes>
 </root>

XSLT:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()" />
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="notes">
          <xsl:apply-templates select="@*" />
   <xsl:copy>
      <xsl:apply-templates select="node()">
         <xsl:sort select="notesBODY/group/text[position() = 0]" />
      </xsl:apply-templates>
   </xsl:copy>
</xsl:template>
    </xsl:stylesheet>

【问题讨论】:

【参考方案1】:

我想你基本上想使用两个模板

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:template match="root">
      <xsl:copy>
          <xsl:apply-templates select="group"/>
          <xsl:apply-templates select=".//notes">
              <xsl:sort select="notesBODY/group/text[1]"/>
          </xsl:apply-templates>
      </xsl:copy>
  </xsl:template>
  
  <xsl:template match="group">
      <xsl:copy>
          <xsl:apply-templates select="* except notes"/>
      </xsl:copy>
  </xsl:template>

显然你已经有了身份转换。

【讨论】:

@Martin Honnen,无论我尝试多么接近,因为我为您提供的解决方案的模拟输入 xml 可以完美地对节点进行排序。对于我的实际输入 xml,我做了一点改动,而不是 @Martin Honnen,我还注意到最终输出中缺少我的实际 xml 中的“组”标签的属性,我该如何复制这些属性? 如果有grouproot 属性之一,则需要使用&lt;xsl:copy&gt;&lt;xsl:apply-templates select="@*"/&gt;...&lt;/xsl:copy&gt;(其中... 是我在上面的代码中显示的)。我不知道为什么您认为您需要更改模板的匹配项,但又希望代码能够完成相同的工作,也许将其作为一个单独的问题提出。 @Martin Honnen,我使用了&lt;xsl:copy-of select="@*" /&gt; 而不是&lt;xsl:apply-templates select="@*"/&gt;,它决定保留属性。这也可以使用吗?对于我发现的模板匹配,使用"\" 是错误的,我使用了group 节点的父节点,它完美运行! 如果您知道只需要复制属性,那么xsl:copy-of 绝对没问题,apply-templates 在使用身份转换作为基本模板的情况下更加通用,一旦您需要更改属性,您只需添加一个模板即可。

以上是关于如何获取具有相同名称的元素并根据 XSLT 中的子节点值对它们进行排序的主要内容,如果未能解决你的问题,请参考以下文章

XSLT:获取元素的前缀?

如何修复 XSLT 3.0 地图问题?

XPath 获取除具有特定名称的子元素之外的所有子元素?

如何使用 XSLT 替换命名空间中的元素?

xslt copy-of到结果文档中的不同命名空间

如何获取具有相同名称的所有元素值