使用XSLT对XML进行排序 - 整个XML模式尚不清楚

use*_*929 7 xml sorting xslt

我想知道如果我不知道整个XML模式,XSLT是否可以对XML文件进行排序.

例如,我想对以下XML文件进行排序.
按/ CATALOG/CD/TITLE排序/ CATALOG/CD元素

<CATALOG attrib1="value1">
  <DVD2>
    <TITLE>The Godfather2</TITLE>
  </DVD2>
  <CD>
    <TITLE>Hide your heart</TITLE>
    <ARTIST>Bonnie Tyler</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>CBS Records</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1988</YEAR>
  </CD>
  <CD attrib4="value4">
    <TITLE>Empire Burlesque</TITLE>
    <ARTIST>Bob Dylan</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>Columbia</COMPANY>
    <PRICE>
      <CATALOG>
        <CD><TITLE>E</TITLE></CD>
        <CD><TITLE>I</TITLE></CD>
        <CD><TITLE>D</TITLE></CD>
      </CATALOG>
    </PRICE>
    <YEAR>1985</YEAR>
  </CD>
  <CD attrib2="value2">
    <TITLE attrib3="value3">Greatest Hits</TITLE>
    <ARTIST>Dolly Parton</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>RCA</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1982</YEAR>
  </CD>
  <DVD>
    <TITLE>The Godfather1</TITLE>
  </DVD>
</CATALOG>
Run Code Online (Sandbox Code Playgroud)

输出应该是:

<CATALOG attrib1="value1">
  <CD attrib4="value4">
    <TITLE>Empire Burlesque</TITLE>
    <ARTIST>Bob Dylan</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>Columbia</COMPANY>
    <PRICE>
      <CATALOG>
        <CD><TITLE>E</TITLE></CD>
        <CD><TITLE>I</TITLE></CD>
        <CD><TITLE>D</TITLE></CD>
      </CATALOG>
    </PRICE>
    <YEAR>1985</YEAR>
  </CD>
  <CD attrib2="value2">
    <TITLE attrib3="value3">Greatest Hits</TITLE>
    <ARTIST>Dolly Parton</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>RCA</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1982</YEAR>
  </CD>
  <CD>
    <TITLE>Hide your heart</TITLE>
    <ARTIST>Bonnie Tyler</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>CBS Records</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1988</YEAR>
  </CD>
  <DVD2>
    <TITLE>The Godfather2</TITLE>
  </DVD2>
  <DVD>
    <TITLE>The Godfather1</TITLE>
  </DVD>
</CATALOG>
Run Code Online (Sandbox Code Playgroud)

以下是我做过的众多尝试之一:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <!--<CATALOG>-->
    <xsl:for-each select="CATALOG/CD">
      <xsl:sort select="TITLE" />
      <xsl:copy-of select="."/>
    </xsl:for-each>
    <!--</CATALOG>-->
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

问题是,使用此XSLT,不会显示CD列表外部的XML部分.
我可以取消注释掉两个已注释掉的代码部分,但这正是我想要避免的.
在这种情况下,如果将任何属性添加到CATALOG元素,则不会将它们复制到输出XML.
我不想重新构建XML文件:我只想知道关于XML模式的某些部分的确切信息.

这个功能很容易实现,例如使用.NET(带有XmlDocument和XmlNode对象)或Python的lxmx库,但是XSLT可以实现吗?

谢谢!

注意:要找到一个样本输入XML并不容易,这样可以避免在所有情况下误解问题.但我会尽可能详细地解决这个问题:

  • 只应对CATALOG下的CD元素进行排序(例如Bob Dylan部分下的CD元素应保持不变)
  • CD以外的元素(例如DVD和DVD2)是否在列表的开头或结尾都是一样的
  • 没有元素,属性,值,注释,因此输出XML中不应缺少任何内容
  • 非CD元素(例如DVD和DVD2)不应按TITLE子元素排序

Emi*_*ggi 1

继续修改身份转换(这可能并不真正安全),我认为以下内容应该相当于@Tim的答案。

注意我根本不提倡这种技术,除非您了解身份转换的一般行为。

<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()[not(self::CD[parent::CATALOG])]"/>
            <xsl:apply-templates select="CD[parent::CATALOG]">
                <xsl:sort select="TITLE"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

或者,如果您关心其他元素DVDDVD2,您可以这样做:

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

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="CD[parent::CATALOG]">
                <xsl:sort select="TITLE"/>
            </xsl:apply-templates>
            <xsl:apply-templates select="node()
                [not(self::CD[parent::CATALOG])]"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)