xslt 将父级 -> 子级更改为子级 -> 父级

xslt change parent -> child to child -> parent

我有一个像这样的 XML,需要用他们的位置改变 parent->child:

<root>
    <testcase name="Case1">
        <testsuite>Suite1</testsuite>
    </testcase>
    <testcase name="Case2">
        <testsuite>Suite1</testsuite>
    </testcase>

    <testcase name="Case3">
        <testsuite>Suite2</testsuite>
    </testcase>
    <testcase name="Case4">
        <testsuite>Suite2</testsuite>
    </testcase>
</root>

需要将其转换为 xml:

<root>
    <testsuite name="Suite1">
        <testcase>Case1</testcase>
        <testcase>Case2</testcase>
    </testsuite>

    <testsuite name="Suite2">
        <testcase>Case3</testcase>
        <testcase>Case4</testcase>
    </testsuite>
</root>

任何人都可以帮助实现这个吗?

这是分组问题的示例,与大多数分组问题一样,首选方法(至少在 XSLT 1.0 中)是使用 Muenchian 分组:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="kSuite" match="testsuite" use="."/>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates 
                      select="testcase/testsuite[generate-id() = 
                                                 generate-id(key('kSuite', .)[1])]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="testsuite">
    <testsuite name="{.}">
      <xsl:apply-templates select="key('kSuite', .)/.." />
    </testsuite>
  </xsl:template>

  <xsl:template match="testcase">
    <xsl:copy>
      <xsl:value-of select="@name" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

当这是 运行 样本输入时,结果是:

<root>
  <testsuite name="Suite1">
    <testcase>Case1</testcase>
    <testcase>Case2</testcase>
  </testsuite>
  <testsuite name="Suite2">
    <testcase>Case3</testcase>
    <testcase>Case4</testcase>
  </testsuite>
</root>

借助像 Saxon 9 或 XmlPrime 这样的 XSLT 2.0 处理器,您可以使用

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

<xsl:output indent="yes"/>

<xsl:template match="/*">
  <xsl:copy>
    <xsl:for-each-group select="*/*" group-by=".">
      <xsl:copy>
        <xsl:attribute name="name" select="."/>
        <xsl:apply-templates select="current-group()/.."/>
      </xsl:copy>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

<xsl:template match="/*/*">
  <xsl:element name="{name()}">
    <xsl:value-of select="@name"/>
  </xsl:element>
</xsl:template>

</xsl:stylesheet>