Xslt <;的自定义格式;xsl:number>;
是否可以为Xslt <;的自定义格式;xsl:number>;,xslt,xslt-2.0,saxon,Xslt,Xslt 2.0,Saxon,是否可以为定义自定义格式 我有这样一种情况,需要一种标准的基于alpha的格式,但字母表中的某些字符是被禁止的(奇怪的要求,但这是客户机所要求的)。例如,字母i不能使用,因此当使用时,我应该得到顺序:a、b、c、d、e、f、g、h、j、k、…、aa、ab、…、ah、aj、 该项目使用的是XSLT2.0和Saxon,因此,如果存在特定于Saxon的解决方案,也可以 XSLT2.0是否提供定义自定义格式序列的功能?Saxon是否提供了注册自定义序列以便与一起使用的功能?XSLT2.0为xsl:num
定义自定义格式
我有这样一种情况,需要一种标准的基于alpha的格式,但字母表中的某些字符是被禁止的(奇怪的要求,但这是客户机所要求的)。例如,字母i
不能使用,因此当使用
时,我应该得到顺序:a、b、c、d、e、f、g、h、j、k、…、aa、ab、…、ah、aj、
该项目使用的是XSLT2.0和Saxon,因此,如果存在特定于Saxon的解决方案,也可以
XSLT2.0是否提供定义自定义格式序列的功能?Saxon是否提供了注册自定义序列以便与
一起使用的功能?XSLT2.0为xsl:number
提供了format
属性,您可以通过该属性使用格式标记aa
。计算的数字取决于value
属性中计算的表达式,并将相应地格式化为format
有鉴于此,您可以考虑首先计算正确的数字序列,排除与特定字母匹配的数字序列
例如,以下说明:
<xsl:number value="$sequence" format="aa"/>
如果$sequence
的计算结果为(注意9
跳过):
请注意,如果您有12个元素,则表达式应该能够跳过不需要的数字(9表示i
),并增加以下一个元素。位置为12的最后一个元素应具有相应的编号13
因此,您需要的只是计算所需序列的算法;这完全取决于您的输入文档
参考资料:编辑:存在另一种更通用的解决方案,并作为单独的答案发布。我留下这个答案,因为它可能对某些人仍然有价值 我喜欢@empo的想法(我修改了它),但我认为可能很难找到有效的解决方案。需要一个聪明的算法/方程式根据原始序列得出正确的序列号,以避免得到不包含禁止字符的标签。在这个时候,这样的算法让我不知所措 我想到的一种方法是创建自己的函数,而不是使用
。本质上,我们处理的是一个基数为23的集合,即字母a
到z
,但不包括字符i
、l
和o
。我提供的函数只能升级到zz
,但这应该足以满足需要(提供多达552个项目的标签)
当我执行上述操作时,我得到以下输出:
<result>
<value n="9">j</value>
<value n="12">n</value>
<value n="15">r</value>
<value n="23">z</value>
<value n="26">ac</value>
<value n="33">ak</value>
<value n="46">az</value>
<value n="69">bz</value>
<value n="70">ca</value>
<value n="200">hs</value>
<value n="552">zz</value>
</result>
J
N
R
Z
交流电
ak
阿兹
bz
ca
hs
zz
如果XSLT能够定义一个自定义字符序列以与
一起使用,那就太好了。似乎这样的功能将通过依赖自定义扩展来推广
w/o,我不知道是否有XSLT引擎提供了
,您可以通过编写接口net.sf.Saxon.lib.Numberer的实现来定制Saxon中xsl:number的输出。可能您希望将其作为net.sf.Saxon.expr.number.Numberer\n的子类。您需要研究源代码并找出需要重写的内容
在Saxon PE/EE中,您可以在Saxon配置文件中注册用于给定语言的编号器。对于Saxon来说,这需要更多的工作:您必须实现接口LocalizerFactory,并用配置注册LocalizerFactory。在发布了我最初的问题解决方案后,我提出了以下更通用的解决方案。该解决方案是纯XSLT,基本上仍然使用
,因此应该适用于任何格式类型
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ewh="http://www.earlhood.com/XSL/Transform"
exclude-result-prefixes="#all">
<!-- Description: XSLT to generate a alpha formatted sequence
label (via <xsl:number>), but disallowing specific characters
from being used.
-->
<!-- Algorithm: Given the index value of the item to generate
a label for via <xsl:number>, we adjust the value so the resulting
label avoids the use of the forbidden characters.
This is achieved by converting the index value into a baseX
number, with X the number of allowed characters.
The baseX number will be converted into a reverse sequence
of numbers for each ^E place. For example, the number 12167
converted to base23 will generate the following reverse sequence:
Place: (23^0, 23^1, 23^2, 23^3)
Sequence: ( 0, 0, 0, 1) // 1000 in base23
Having it in right-to-left order makes processing easier.
Each item in the sequence will be a number from 0 to baseX-1.
With the sequence, we can then just call <xsl:number> on
each item and reverse concatenate the result.
NOTE: Since <xsl:number> does not like 0 as a given value,
the sequence must be processed so each item is within the
range of 1-to-baseX. For example, the above base23 example
will be translated to the following:
(23, 22, 22)
-->
<xsl:output method="xml" indent="yes"/>
<!-- Number of allowed characters: This should be total number of chars of
format-type desired minus the chars that should be skipped. -->
<xsl:variable name="lbase" select="23"/>
<!-- Sequence of character positions not allowed, with 1=>a to 26=>z -->
<xsl:variable name="lexcs" select="(9,12,15)"/> <!-- i,l,o -->
<!-- Helper Function:
Convert integer to sequence of number of given base.
The sequence of numbers is in reverse order: ^0,^1,^2,...^N.
-->
<xsl:function name="ewh:get_base_digits" as="item()*">
<xsl:param name="number" as="xs:integer"/>
<xsl:param name="to" as="xs:integer"/>
<xsl:variable name="Q" select="$number idiv $to"/>
<xsl:variable name="R" select="$number mod $to"/>
<xsl:sequence select="$R"/>
<xsl:if test="$Q gt 0">
<xsl:sequence select="ewh:get_base_digits($Q,$to)"/>
</xsl:if>
</xsl:function>
<!-- Helper Function:
Compute carry-overs in reverse-base digit sequence. XSLT starts
numbering at 1, so we cannot have any 0s.
-->
<xsl:function name="ewh:compute_carry_overs" as="item()*">
<xsl:param name="digits" as="item()*"/>
<xsl:variable name="d" select="subsequence($digits, 1, 1)"/>
<xsl:choose>
<xsl:when test="($d le 0) and (count($digits) = 1)">
<!-- 0 at end of list, nothing to do -->
</xsl:when>
<xsl:when test="$d le 0">
<!-- If digit <=0, need to perform carry-over operation -->
<xsl:variable name="next" select="subsequence($digits, 2, 1)"/>
<xsl:choose>
<xsl:when test="count($digits) le 2">
<xsl:sequence select="$lbase + $d"/>
<xsl:sequence select="ewh:compute_carry_overs($next - 1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$lbase + $d"/>
<xsl:sequence select="ewh:compute_carry_overs(($next - 1,
subsequence($digits, 3)))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="count($digits) le 1">
<xsl:sequence select="$d"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$d"/>
<xsl:sequence select="ewh:compute_carry_overs(subsequence($digits, 2))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!-- Helper Function:
Given a number in the base range, determine number for
purposes of <xsl:number>. We loop thru the exclusion
list and add 1 for each exclusion letter that has
been passed. The $digit parameter should be a number
in the range [1..$lbase].
-->
<xsl:function name="ewh:compute_digit_offset" as="xs:integer">
<xsl:param name="digit" as="xs:integer"/>
<xsl:param name="excludes" as="item()*"/>
<xsl:variable name="l" select="subsequence($excludes, 1, 1)"/>
<xsl:variable name="result">
<xsl:choose>
<xsl:when test="$digit lt $l">
<xsl:value-of select="0"/>
</xsl:when>
<xsl:when test="count($excludes) = 1">
<xsl:value-of select="1"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="rest">
<xsl:value-of select="ewh:compute_digit_offset($digit+1,
subsequence($excludes,2))"/>
</xsl:variable>
<xsl:value-of select="1 + $rest"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$result"/>
</xsl:function>
<!-- Retrieve alpha sequence label.
This is the main function to call.
-->
<xsl:function name="ewh:get-alpha-label" as="xs:string">
<xsl:param name="number" as="xs:integer"/>
<xsl:variable name="basedigits"
select="ewh:get_base_digits($number,$lbase)"/>
<xsl:variable name="digits"
select="ewh:compute_carry_overs($basedigits)"/>
<xsl:variable name="result" as="item()*">
<xsl:for-each select="$digits">
<xsl:variable name="digit" select="."/>
<!-- Should not have any 0 values. If some reason we do,
we ignore assuming they are trailing items. -->
<xsl:if test="$digit != 0">
<xsl:variable name="value">
<xsl:value-of select="$digit +
ewh:compute_digit_offset($digit,$lexcs)"/>
</xsl:variable>
<xsl:variable name="number">
<xsl:number value="$value" format="a"/>
</xsl:variable>
<xsl:sequence select="$number"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string-join(reverse($result),'')"/>
</xsl:function>
<!-- For testing -->
<xsl:template match="/">
<result>
<xsl:for-each select="(1 to 1000,12166,12167,12168,279840,279841,279842)">
<value n="{.}"><xsl:value-of select="ewh:get-alpha-label(.)"/></value>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
有趣的问题+1。我喜欢基本方法,但必须考虑如何正确生成序列。要排除的字母与输入无关。例如,字母i
、l
和o
不能出现在生成的
中,包括数字变成双字符时。不确定将原始序列号映射到有效序列号的算法有多简单。例如,要以双字母形式排除i
,应跳过数字34(9+26)。您可以发布一个要处理的XML示例,这里的一些专家会提供帮助。顺序是:x需要像有序列表一样标记的项目数,但标签不包括特定的字母字符。“跳过”并不像你想象的那么简单<代码>z表示项目23。项目34将有标签am
。丢失的字母每23项增加一次移位,但实际移位在不同的周期中有所不同,因为我需要排除3个字母。请参阅我对一般问题的回答,但它没有使用
。我发现了一个适用于任意数量项的数值解,并且可以轻松地与xsl:number
集成(如我的回答所示)。你还对这种解决方案感兴趣吗?是的,我感兴趣。实际上,我已经提出了一个不同的解决方案,它没有数值解
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ewh="http://www.earlhood.com/XSL/Transform"
exclude-result-prefixes="#all">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="letters" select="'abcdefghjkmnpqrstuvwxyz'"/>
<xsl:variable name="lbase" select="23"/>
<xsl:function name="ewh:get-alpha-label" as="xs:string">
<xsl:param name="number" as="xs:integer"/>
<xsl:variable name="quotient" select="$number idiv $lbase"/>
<xsl:variable name="remainder" select="$number mod $lbase"/>
<xsl:variable name="p1">
<xsl:choose>
<xsl:when test="($quotient gt 0) and ($remainder = 0)">
<xsl:value-of select="substring($letters,($quotient - 1),1)"/>
</xsl:when>
<xsl:when test="($quotient gt 0) and ($remainder gt 0)">
<xsl:value-of select="substring($letters,$quotient,1)"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="p0">
<xsl:choose>
<xsl:when test="$remainder = 0">
<xsl:value-of select="substring($letters,$lbase,1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($letters,$remainder,1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat($p1,$p0)"/>
</xsl:function>
<xsl:template match="/">
<result>
<value n="9"><xsl:value-of select="ewh:get-alpha-label(9)"/></value>
<value n="12"><xsl:value-of select="ewh:get-alpha-label(12)"/></value>
<value n="15"><xsl:value-of select="ewh:get-alpha-label(15)"/></value>
<value n="23"><xsl:value-of select="ewh:get-alpha-label(23)"/></value>
<value n="26"><xsl:value-of select="ewh:get-alpha-label(26)"/></value>
<value n="33"><xsl:value-of select="ewh:get-alpha-label(33)"/></value>
<value n="46"><xsl:value-of select="ewh:get-alpha-label(46)"/></value>
<value n="69"><xsl:value-of select="ewh:get-alpha-label(69)"/></value>
<value n="70"><xsl:value-of select="ewh:get-alpha-label(70)"/></value>
<value n="200"><xsl:value-of select="ewh:get-alpha-label(200)"/></value>
<value n="552"><xsl:value-of select="ewh:get-alpha-label(552)"/></value>
</result>
</xsl:template>
</xsl:stylesheet>
<result>
<value n="9">j</value>
<value n="12">n</value>
<value n="15">r</value>
<value n="23">z</value>
<value n="26">ac</value>
<value n="33">ak</value>
<value n="46">az</value>
<value n="69">bz</value>
<value n="70">ca</value>
<value n="200">hs</value>
<value n="552">zz</value>
</result>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ewh="http://www.earlhood.com/XSL/Transform"
exclude-result-prefixes="#all">
<!-- Description: XSLT to generate a alpha formatted sequence
label (via <xsl:number>), but disallowing specific characters
from being used.
-->
<!-- Algorithm: Given the index value of the item to generate
a label for via <xsl:number>, we adjust the value so the resulting
label avoids the use of the forbidden characters.
This is achieved by converting the index value into a baseX
number, with X the number of allowed characters.
The baseX number will be converted into a reverse sequence
of numbers for each ^E place. For example, the number 12167
converted to base23 will generate the following reverse sequence:
Place: (23^0, 23^1, 23^2, 23^3)
Sequence: ( 0, 0, 0, 1) // 1000 in base23
Having it in right-to-left order makes processing easier.
Each item in the sequence will be a number from 0 to baseX-1.
With the sequence, we can then just call <xsl:number> on
each item and reverse concatenate the result.
NOTE: Since <xsl:number> does not like 0 as a given value,
the sequence must be processed so each item is within the
range of 1-to-baseX. For example, the above base23 example
will be translated to the following:
(23, 22, 22)
-->
<xsl:output method="xml" indent="yes"/>
<!-- Number of allowed characters: This should be total number of chars of
format-type desired minus the chars that should be skipped. -->
<xsl:variable name="lbase" select="23"/>
<!-- Sequence of character positions not allowed, with 1=>a to 26=>z -->
<xsl:variable name="lexcs" select="(9,12,15)"/> <!-- i,l,o -->
<!-- Helper Function:
Convert integer to sequence of number of given base.
The sequence of numbers is in reverse order: ^0,^1,^2,...^N.
-->
<xsl:function name="ewh:get_base_digits" as="item()*">
<xsl:param name="number" as="xs:integer"/>
<xsl:param name="to" as="xs:integer"/>
<xsl:variable name="Q" select="$number idiv $to"/>
<xsl:variable name="R" select="$number mod $to"/>
<xsl:sequence select="$R"/>
<xsl:if test="$Q gt 0">
<xsl:sequence select="ewh:get_base_digits($Q,$to)"/>
</xsl:if>
</xsl:function>
<!-- Helper Function:
Compute carry-overs in reverse-base digit sequence. XSLT starts
numbering at 1, so we cannot have any 0s.
-->
<xsl:function name="ewh:compute_carry_overs" as="item()*">
<xsl:param name="digits" as="item()*"/>
<xsl:variable name="d" select="subsequence($digits, 1, 1)"/>
<xsl:choose>
<xsl:when test="($d le 0) and (count($digits) = 1)">
<!-- 0 at end of list, nothing to do -->
</xsl:when>
<xsl:when test="$d le 0">
<!-- If digit <=0, need to perform carry-over operation -->
<xsl:variable name="next" select="subsequence($digits, 2, 1)"/>
<xsl:choose>
<xsl:when test="count($digits) le 2">
<xsl:sequence select="$lbase + $d"/>
<xsl:sequence select="ewh:compute_carry_overs($next - 1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$lbase + $d"/>
<xsl:sequence select="ewh:compute_carry_overs(($next - 1,
subsequence($digits, 3)))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="count($digits) le 1">
<xsl:sequence select="$d"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$d"/>
<xsl:sequence select="ewh:compute_carry_overs(subsequence($digits, 2))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!-- Helper Function:
Given a number in the base range, determine number for
purposes of <xsl:number>. We loop thru the exclusion
list and add 1 for each exclusion letter that has
been passed. The $digit parameter should be a number
in the range [1..$lbase].
-->
<xsl:function name="ewh:compute_digit_offset" as="xs:integer">
<xsl:param name="digit" as="xs:integer"/>
<xsl:param name="excludes" as="item()*"/>
<xsl:variable name="l" select="subsequence($excludes, 1, 1)"/>
<xsl:variable name="result">
<xsl:choose>
<xsl:when test="$digit lt $l">
<xsl:value-of select="0"/>
</xsl:when>
<xsl:when test="count($excludes) = 1">
<xsl:value-of select="1"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="rest">
<xsl:value-of select="ewh:compute_digit_offset($digit+1,
subsequence($excludes,2))"/>
</xsl:variable>
<xsl:value-of select="1 + $rest"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$result"/>
</xsl:function>
<!-- Retrieve alpha sequence label.
This is the main function to call.
-->
<xsl:function name="ewh:get-alpha-label" as="xs:string">
<xsl:param name="number" as="xs:integer"/>
<xsl:variable name="basedigits"
select="ewh:get_base_digits($number,$lbase)"/>
<xsl:variable name="digits"
select="ewh:compute_carry_overs($basedigits)"/>
<xsl:variable name="result" as="item()*">
<xsl:for-each select="$digits">
<xsl:variable name="digit" select="."/>
<!-- Should not have any 0 values. If some reason we do,
we ignore assuming they are trailing items. -->
<xsl:if test="$digit != 0">
<xsl:variable name="value">
<xsl:value-of select="$digit +
ewh:compute_digit_offset($digit,$lexcs)"/>
</xsl:variable>
<xsl:variable name="number">
<xsl:number value="$value" format="a"/>
</xsl:variable>
<xsl:sequence select="$number"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string-join(reverse($result),'')"/>
</xsl:function>
<!-- For testing -->
<xsl:template match="/">
<result>
<xsl:for-each select="(1 to 1000,12166,12167,12168,279840,279841,279842)">
<value n="{.}"><xsl:value-of select="ewh:get-alpha-label(.)"/></value>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>