3

我正在尝试制作一个 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 "×1"' (16121)。

我试图将它隔离到第一个数字(所以在 'Joint' 和 '"' 之间)并且只有在第一个数字没有分数的情况下才真正起作用。

我的另一个想法是可以让 xslt 将分数转换为小数吗?

我到底将如何使用 XSLT 对它进行排序?

4

1 回答 1

3

免责声明:这完全是疯狂的。

<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, '&quot;', '')" />
    <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()扩展功能,才能真正在您的样式表中使用它。

当然,只要输入格式略有变化,这就会分崩离析。


以上内容可以作为一个例子,说明为什么以强调值和结构的数据格式存储格式化的、不透明的字符串是最终的坏主意。

于 2013-11-10T23:04:40.777 回答