XSLT 对指定但非唯一的数据字段进行排序和编号

XSLT sort and number on specified, but non-unique data fields

我有一个来自 FileMaker 的输入 XML 文件,所以我无法控制 XML 元素的命名等。事实上,这个 XML 基本上是一个 xml 化的 CSV,数据如下所示:

    <RESULTSET FOUND="1">
    <ROW MODID="0" RECORDID="123">
        <COL>
            <DATA>xxx</DATA>
        </COL>
        <COL>
            <DATA><<<</DATA>
        </COL>
        <COL>
            <DATA>6</DATA>
            <DATA>500</DATA>
            <DATA>40</DATA>
        </COL>
        <COL>
            <DATA>10</DATA>
            <DATA>41</DATA>
            <DATA>13</DATA>
        </COL>

最后两个字段是我关心的。如您所见,它们没有排序,据我所知,不可能从 FM 中导出排序的它们,所以我需要在我的 XSLT 中对它们进行排序。这两个是键值对,只是FM是这样导出的。在语义上,我有:

它们在XML内的位置是有保证的,所以目前我的转换是:

        <xsl:if test="fmp:COL[10]/fmp:DATA[1]">
            <entry key="key1"><xsl:value-of select="fmp:COL[10]/fmp:DATA[1]"/></entry>
            <entry key="value1"><xsl:value-of select="fmp:COL[11]/fmp:DATA[1]"/></entry>
        </xsl:if>
        <xsl:if test="fmp:COL[10]/fmp:DATA[2]">
            <entry key="key2"><xsl:value-of select="fmp:COL[10]/fmp:DATA[2]"/></entry>
            <entry key="value2"><xsl:value-of select="fmp:COL[11]/fmp:DATA[2]"/></entry>
        </xsl:if>
        (...)

这很丑陋而且不完美,但我被困在这里。我愿意:

a) 遍历它们,以编程方式生成“key1”、“key2”等名称(以便它适用于任意数量的条目) b) 从第一个值(键)的低到高对它们进行排序,以便保证这些对保持在一起

这两个我一辈子都想不通。如果我有一个唯一的元素标识符,我已经找到足够多的关于 XSLT:sort 的信息,但是没有任何东西可以对对进行排序。

我不是 XSLT 专家,但到目前为止我已经弄明白了所有其他事情,所以即使是正确方向的指示也会有所帮助。

考虑以下示例:

XML

<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">

    <!-- omitted -->

    <RESULTSET>
        <ROW>
            <COL/>
            <COL/>
            <COL>
                <DATA>6</DATA>
                <DATA>500</DATA>
                <DATA>40</DATA>
            </COL>
            <COL>
                <DATA>10</DATA>
                <DATA>41</DATA>
                <DATA>13</DATA>
            </COL>
        </ROW>
    </RESULTSET>
</FMPXMLRESULT>

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="fmp exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/fmp:FMPXMLRESULT">
    <output>
        <xsl:for-each select="fmp:RESULTSET/fmp:ROW">
            <record>

                <!-- other data -->

                <xsl:variable name="pairs">
                    <xsl:for-each select="fmp:COL[3]/fmp:DATA">
                        <xsl:variable name="i" select="position()" />
                        <entry key="{.}">
                            <xsl:value-of select="../../fmp:COL[4]/fmp:DATA[$i]"/>
                        </entry>
                    </xsl:for-each>
                </xsl:variable>
                <xsl:for-each select="exsl:node-set($pairs)/entry">
                    <xsl:sort select="@key" data-type="number"/>
                    <xsl:copy-of select="."/>
                </xsl:for-each>
            </record>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

结果

<?xml version="1.0" encoding="utf-8"?>
<output>
  <record>
    <entry key="6">10</entry>
    <entry key="40">13</entry>
    <entry key="500">41</entry>
  </record>
</output>

旁注:

AFAIK it is impossible to export them sorted from FM

这要看这个数据是从哪里来的:如果是相关记录,那当然可以排序关系;如果它是重复字段(希望不是!),那么它可能会更加困难 - 尽管我认为这并非不可能。

无论如何,如果排序的唯一目的是为了导出,那么恕我直言,最好将排序留给样式表,如上所示。

这是您可以查看的另一种方式:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
exclude-result-prefixes="fmp">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="value" match="fmp:COL[4]/fmp:DATA" use="concat(count(preceding-sibling::fmp:DATA), '|', generate-id(ancestor::fmp:ROW))" />

<xsl:template match="/fmp:FMPXMLRESULT">
    <output>
        <xsl:for-each select="fmp:RESULTSET/fmp:ROW">
            <record>
            
                <!-- other data -->

                <xsl:for-each select="fmp:COL[3]/fmp:DATA">
                    <xsl:sort select="." data-type="number"/>
                        <entry key="{.}">
                            <xsl:value-of select="key('value', concat(count(preceding-sibling::fmp:DATA), '|', generate-id(ancestor::fmp:ROW)))"/>
                        </entry>
                </xsl:for-each>
            </record>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>