xsl:用英制测量值排序

时间:2013-11-10 20:48:15

标签: xml sorting xslt xslt-1.0

我正在尝试创建一个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)。

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

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

我将如何使用XSLT对此进行排序?

1 个答案:

答案 0 :(得分: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()扩展功能,以实际使其在样式表中可用。

当输入格式稍微改变时,这当然就会崩溃。


上面可以举例说明为什么以强调价值和结构的数据格式存储格式化的不透明字符串是最终的坏主意。