使用 XSLT 3.0 按字母顺序对 KML 地标进行排序

Sorting KML placemarks alphabetically using XSLT 3.0

我正在尝试按字母顺序对 KML 文档中的地标元素进行排序。该文档包含多个我不想重新排序的 Folder 元素。我只需要对每个文件夹内的地标进行排序。

示例输入:

<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Test</name>
    <Folder>
      <name>Zulu</name>
      <Placemark>
        <name>Bravo</name>
        <Point>
          <coordinates>
            20,30,0
          </coordinates>
        </Point>
      </Placemark>
      <Placemark>
        <name>Alfa</name>
        <Point>
          <coordinates>
            10,20,0
          </coordinates>
        </Point>
      </Placemark>
    </Folder>
    <Folder>
      <name>Yankee</name>
      <Placemark>
        <name>Delta</name>
        <Point>
          <coordinates>
            40,50,0
          </coordinates>
        </Point>
      </Placemark>
      <Placemark>
        <name>Charlie</name>
        <Point>
          <coordinates>
            30,40,0
          </coordinates>
        </Point>
      </Placemark>
    </Folder>
  </Document>
</kml>

预期输出:

<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Test</name>
    <Folder>
      <name>Zulu</name>
      <Placemark>
        <name>Alfa</name>
        <Point>
          <coordinates>
            10,20,0
          </coordinates>
        </Point>
      </Placemark>
      <Placemark>
        <name>Bravo</name>
        <Point>
          <coordinates>
            20,30,0
          </coordinates>
        </Point>
      </Placemark>
    </Folder>
    <Folder>
      <name>Yankee</name>
      <Placemark>
        <name>Charlie</name>
        <Point>
          <coordinates>
            30,40,0
          </coordinates>
        </Point>
      </Placemark>
      <Placemark>
        <name>Delta</name>
        <Point>
          <coordinates>
            40,50,0
          </coordinates>
        </Point>
      </Placemark>
    </Folder>
  </Document>
</kml>

我尝试了以下 XSLT 3.0 样式表:

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

  <xsl:output name="xml"/>

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

  <xsl:template match="Folder">
    <xsl:perform-sort select="Folder/Placemark">
      <xsl:sort select="name" data-type="text" order="ascending"/>
    </xsl:perform-sort>
  </xsl:template>

</xsl:stylesheet>

它什么也没做。我正在尝试使用 XSLT 3.0,因为它是最新的规范。我正在使用 Saxon 来 运行 转换(以防万一):

java -jar Saxon-HE-10.0.jar -s:source.kml -xsl:sort.xsl -o:output.kml

如果有人知道如何使用 XSLT 1.0 或 2.0 执行此操作,我会接受,但我更喜欢 XSLT 3.0。

干杯

从 Saxon 10 开始,例如match="Folder" 匹配命名空间 http://www.opengis.net/kml/2.2 中的元素,可以使用命令行选项 -ns:http://www.opengis.net/kml/2.2,参见 http://saxonica.com/html/documentation/using-xsl/commandline/

当然,在 XSLT 2 及更高版本中,您还可以在 xsl:stylesheet 元素上设置 xpath-default-namespace="http://www.opengis.net/kml/2.2"

perform-sort好像错了select和Folder/Placemark,我觉得应该是Placemark,但是只排序[=19=的任务] elements 可能无法按照您设置模板的方式解决。解决方案的复杂性取决于输入的可能复杂性,Placemark 元素是否可以与其他子元素混合;如果您知道所有其他元素都在地标子元素之前,那么使用

就足够了
<xsl:template match="Folder">
    <xsl:copy>
        <xsl:apply-templates select="* except Placemark, sort(Placemark, (), function($p) { $p/name })"/>
    </xsl:copy>
</xsl:template>

在 XSLT 3 和 Saxon 10 HE 或更早版本的 Saxon 中,但 PE 或 EE。

如果 Placemark 元素都相邻则

<xsl:template match="Folder">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:for-each-group select="*" group-adjacent=". instance of element(Placemark)">
            <xsl:apply-templates
              select="if (current-grouping-key())
                      then sort(current-group(), (), function($p) { $p/name })
                      else current-group()"/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

应该足够了。

一个非常基本的方法是在 xsl:stylesheet 元素上定义一个名称空间并使用它("select" 表达式是错误的):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:kml="http://www.opengis.net/kml/2.2">
  <xsl:output name="xml"/>

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

  <xsl:template match="kml:Folder">
    <xsl:copy>
      <xsl:copy-of select="* except kml:Placemark" />
      <xsl:perform-sort select="kml:Placemark">
        <xsl:sort select="kml:name" data-type="text" order="ascending"/>
      </xsl:perform-sort>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>