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是这样导出的。在语义上,我有:
- 6:10
- 40:13
- 500:41
它们在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>
我有一个来自 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是这样导出的。在语义上,我有:
- 6:10
- 40:13
- 500:41
它们在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>