将XSLT中的字母数字与破折号组合

时间:2018-11-07 10:44:51

标签: xslt

<ReferenceDesignators>
<ReferenceDesignator>R1</ReferenceDesignator>
<ReferenceDesignator>R2</ReferenceDesignator>
<ReferenceDesignator>R3</ReferenceDesignator>
<ReferenceDesignator>R4</ReferenceDesignator>
<ReferenceDesignator>R5</ReferenceDesignator>
<ReferenceDesignator>R6</ReferenceDesignator>
<ReferenceDesignator>R7</ReferenceDesignator>
<ReferenceDesignator>R8</ReferenceDesignator>
<ReferenceDesignator>R9</ReferenceDesignator>
<ReferenceDesignator>R10</ReferenceDesignator>
<ReferenceDesignator>R17</ReferenceDesignator>
<ReferenceDesignator>SMD</ReferenceDesignator>
</ReferenceDesignators>

大家好, 请参阅上面的XML。我正在生成PDF并编写了如下的XSL代码。

<xsl:for-each select="ReferenceDesignator">
<xsl:value-of select="."/>
<xsl:if test ="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>

因此,我得到所有用逗号分隔的值。但是我希望输出为 R1-R10,R17,SMD。

如何通过正则表达式实现此目标? 请帮忙。

谢谢, 万寿

2 个答案:

答案 0 :(得分:2)

一种可能的方法是创建一个函数,该函数可用于向值加1(如果值中包含数字)

<xsl:function name="my:check">
  <xsl:param name="current" />
  <xsl:variable name="number" select="if (matches($current, '.*\d+')) then xs:int(replace($current, '[A-Z]+', '')) + 1 else ''" />
  <xsl:value-of select="replace($current, '\d', ''), $number" separator="" />
</xsl:function>

然后,您可以使用xsl:for-each-group对元素进行分组,这些元素以与上一个值不连续的元素开头

<xsl:for-each-group select="ReferenceDesignator" group-starting-with="ReferenceDesignator[. != my:check(preceding-sibling::ReferenceDesignator[1])]">

尝试使用此XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:my="my">

  <xsl:output method="text" />

  <xsl:template match="ReferenceDesignators">
    <xsl:for-each-group select="ReferenceDesignator" group-starting-with="ReferenceDesignator[. != my:check(preceding-sibling::ReferenceDesignator[1])]">
      <xsl:if test="position() > 1">,</xsl:if>
      <xsl:value-of select="current-group()[1], current-group()[position() > 1][last()]" separator="-" />
    </xsl:for-each-group>
  </xsl:template>

  <xsl:function name="my:check">
    <xsl:param name="current" />
    <xsl:variable name="number" select="if (matches($current, '.*\d+')) then xs:int(replace($current, '[A-Z]+', '')) + 1 else ''" />
    <xsl:value-of select="replace($current, '\d', ''), $number" separator="" />
  </xsl:function>
</xsl:stylesheet>

请注意,这确实是假设每个ReferenceDesignator由一个或多个字母组成,在更多的数字后跟零。

答案 1 :(得分:2)

作为替代方案,以下是使用for-each-group group-adjacent的XSLT 3模板:

  <xsl:template match="ReferenceDesignators">
      <xsl:value-of separator=",">
          <xsl:for-each-group 
            select="ReferenceDesignator" composite="yes"
            group-adjacent="let $p := position(), 
                            $comps := analyze-string(., '(\p{L}+)([0-9]+)'), 
                            $prefix := $comps//*:group[@nr = 1]/data(), $i := $comps//*:group[@nr = 2]/data() 
                            return ($prefix, $i!(xs:integer(.) - $p))">
              <xsl:sequence
              select="string-join((. | current-group()[last()]), '-')"/>
          </xsl:for-each-group>          
      </xsl:value-of>
  </xsl:template>

https://xsltfiddle.liberty-development.net/eiZQaGg/3

最好将group-adjacent属性中的表达式分解为一个函数:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:function name="mf:grouping-key" as="item()+">
      <xsl:param name="value" as="xs:string"/>
      <xsl:param name="pos" as="xs:integer"/>
      <xsl:sequence
        select="let $comps := analyze-string($value, '(\p{L}+)([0-9]+)?'),
                    $prefix := $comps//*:group[@nr = 1]/data(),
                    $suffix := $comps//*:group[@nr = 2]!(xs:integer(.) - $pos)
                return ($prefix, $suffix)"/>
  </xsl:function>

  <xsl:template match="ReferenceDesignators">
      <xsl:value-of separator=",">
          <xsl:for-each-group select="ReferenceDesignator" composite="yes" group-adjacent="mf:grouping-key(., position())">
              <xsl:sequence
              select="string-join((. | current-group()[last()]), '-')"/>
          </xsl:for-each-group>          
      </xsl:value-of>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/eiZQaGg/4

在XSLT 2中,您可以使用串联的分组密钥而不是组合密钥:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="2.0">

  <xsl:function name="mf:grouping-key" as="xs:string">
      <xsl:param name="value" as="xs:string"/>
      <xsl:param name="pos" as="xs:integer"/>
      <xsl:variable name="comps" as="item()*">
          <xsl:variable name="pattern" as="xs:string">(\p{L}+)([0-9]+)?</xsl:variable>
          <xsl:analyze-string select="$value" regex="{$pattern}">
              <xsl:matching-substring>
                  <xsl:sequence select="regex-group(1), regex-group(2)"/>
              </xsl:matching-substring>
          </xsl:analyze-string>
      </xsl:variable>
      <xsl:variable name="prefix" select="$comps[1]"/>
      <xsl:variable name="suffix" select="if ($comps[2] castable as xs:integer) then (xs:integer($comps[2]) - $pos) else ()"/>
      <xsl:sequence
        select="concat($prefix, $suffix)"/>
  </xsl:function>

  <xsl:template match="ReferenceDesignators">
      <xsl:value-of separator=",">
          <xsl:for-each-group select="ReferenceDesignator" group-adjacent="mf:grouping-key(., position())">
              <xsl:sequence
              select="string-join((. | current-group()[last()]), '-')"/>
          </xsl:for-each-group>          
      </xsl:value-of>
  </xsl:template>

</xsl:stylesheet>

http://xsltransform.hikmatu.com/3NzcBsE/1