我正在尝试制作一个 xslt 文件来对 InDesign 的列表进行排序。
我遇到的问题是,当大小为英制而不是公制时,试图让列表按预期排序。更复杂的是,大小信息包含在字符串中。
这是 XSLT:
<CATEGORY>
<VERS>
<xsl:for-each select="VFPData/g_otemp/prodid">
<xsl:sort select="floor(translate(../desc,$vDigits,''))" />
<xsl:sort select="translate(../desc,$vAlpha,'')" data-type="number"/>
<xsl:if test="../inactive != 'true'">
<code><xsl:value-of select="../prodid" /></code>
<name><xsl:value-of select="../desc" /></name>
<xsl:if test="../priceout != '0.0000'">
<price><xsl:value-of select="format-number(../priceout, '£0.00')" /></price>
</xsl:if>
<xsl:if test="../priceout = '0.0000'">
<price>P.O.A</price>
</xsl:if>
</xsl:if>
</xsl:for-each>
</VERS>
</CATEGORY>
这在处理公制大小时工作正常,但是对于英制,您会看到这样的排序:
<VERS>
<code>BM50-100</code>
<name>Blue-Max Joint 16" x 12" x 1"</name>
<price>£31.82</price>
<code>BM50-106</code>
<name>Blue-Max Joint 11" x 8 1/2" x 1"</name>
<price>£24.33</price>
<code>BM50-123</code>
<name>Blue-Max Joint 2 1/2" x 2" x 1/2"</name>
<price>£7.42</price>
<code>BM50-133</code>
<name>Blue-Max Joint 2 3/4" x 2" x 1/2"</name>
</VERS>
它应该在哪里:
<VERS>
<code>BM50-123</code>
<name>Blue-Max Joint 2 1/2" x 2" x 1/2"</name>
<price>£7.42</price>
<code>BM50-133</code>
<name>Blue-Max Joint 2 3/4" x 2" x 1/2"</name>
<code>BM50-106</code>
<name>Blue-Max Joint 11" x 8 1/2" x 1"</name>
<price>£24.33</price>
<code>BM50-100</code>
<name>Blue-Max Joint 16" x 12" x 1"</name>
<price>£31.82</price>
</VERS>
大概发生这种情况是因为它排序的值是字符串中所有数字的组合,这意味着'2 3/4" x 2" x 1/2"' = 234212使其确实大于'16" x 12" x 1"' (16121)。
我试图将其隔离到第一个数字(因此在"联合"和"之间),并且只有在第一个数字没有分数时才真正有效。
我的另一个想法是,是否有可能让 xslt 将分数转换为小数?
我到底该如何使用 XSLT 对此进行排序?
免责声明:这完全是疯狂的。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="name">
<p>
<xsl:apply-templates select="." mode="sort" />
</p>
</xsl:template>
<!-- finds the first digit in a name and then extracts exactly thee numerical values -->
<xsl:template match="name" mode="sort">
<xsl:param name="rest" select="string(.)" />
<xsl:variable name="left" select="substring($rest, 1, 1)" />
<xsl:variable name="by" select="' x '" />
<xsl:if test="$left">
<xsl:choose>
<xsl:when test="number($left)">
<xsl:variable name="s1" select="substring-before($rest, $by)" />
<xsl:variable name="s2" select="substring-before(substring-after($rest, $by), $by)" />
<xsl:variable name="s3" select="substring-after($rest, concat($s1, $by, $s2, $by))" />
<xsl:variable name="n1">
<xsl:call-template name="sanitize"><xsl:with-param name="str" select="$s1" /></xsl:call-template>
</xsl:variable>
<xsl:variable name="n2">
<xsl:call-template name="sanitize"><xsl:with-param name="str" select="$s2" /></xsl:call-template>
</xsl:variable>
<xsl:variable name="n3">
<xsl:call-template name="sanitize"><xsl:with-param name="str" select="$s3" /></xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(
format-number($n1, '000.00'), ' - ',
format-number($n2, '000.00'), ' - ',
format-number($n3, '000.00')
) " />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="." mode="sort">
<xsl:with-param name="rest" select="substring-after($rest, $left)" />
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- converts strings of one full and one factional number ("2 1/4") to a decimal number (2.25) -->
<xsl:template name="sanitize">
<xsl:param name="str" />
<xsl:variable name="bare" select="translate($str, '"', '')" />
<xsl:variable name="full">
<xsl:choose>
<xsl:when test="contains($bare, '/')">
<xsl:value-of select="substring-before($bare, ' ')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$bare" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="frac" select="substring-after($bare, $full)" />
<xsl:variable name="fullNum">
<xsl:choose>
<xsl:when test="number($full)">
<xsl:value-of select="number($full)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="fracNum">
<xsl:choose>
<xsl:when test="$frac">
<xsl:variable name="q" select="number(substring-before($frac, '/'))" />
<xsl:variable name="d" select="number(substring-after($frac, '/'))" />
<xsl:choose>
<xsl:when test="$q and $d">
<xsl:value-of select="$q div $d" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(concat('0.', $q))" /><!-- this is debatable -->
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$fullNum + $fracNum" />
</xsl:template>
</xsl:stylesheet>
当应用于时
<test>
<name>Blue-Max Joint 16" x 12" x 1"</name>
<name>Blue-Max Joint 11" x 8 1/2" x 1"</name>
<name>Blue-Max Joint 2 1/2" x 2" x 1/2"</name>
<name>Blue-Max Joint 2 3/4" x 2" x 1/2"</name>
</test>
给你
<p>016.00 - 012.00 - 001.00</p>
<p>011.00 - 008.50 - 001.00</p>
<p>002.50 - 002.00 - 000.50</p>
<p>002.75 - 002.00 - 000.50</p>
现在,您可以使用这些字符串进行排序。即使是三向排序也会起作用。
但是你需要做一些额外的工作,可能涉及node-set()
扩展函数,才能真正使其在你的样式表中可用。
当然,一旦输入格式仅略有变化,这种情况就会崩溃。
可以作为一个例子,说明为什么以强调值和结构的数据格式存储格式化的不透明字符串是最终的坏主意。