我正在使用xslt和Saxon HE,我使用指定的方法here来生成UUID,但是我为同一级别的所有节点获得了相同的id。
我现在得到的是什么:
<aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
<discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
<li id="#d2e17">Dark Coffee<a href="#d2e20">USD 1.8</a>
</li>
</discount>
</aaa>
<aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
<discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
<li id="#d2e32">Milk Shake<a href="#d2e35">USD 2.6</a>
</li>
</discount>
</aaa>
<aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
<discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
<li id="#d2e47">Iced Coffee<a href="#d2e50">USD 1.5</a>
</li>
</discount>
</aaa>
<aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
<discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
<li id="#d2e62">Bottled Water<a href="#d2e65">USD 2.5</a>
</li>
</discount>
</aaa>
我希望每个aaa
代码和discount
代码的ID彼此唯一。我应该做些什么改变来实现这一目标。
以下是我使用的xslt。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:m0="http://services.samples" xmlns:math="http://exslt.org/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:uuid="http://www.uuid.org" version="2.0" exclude-result-prefixes="m0 fn">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="drink_name"/>
<xsl:template match="/">
<Payment xmlns="http://ws.apache.org/ns/synapse">
<xsl:for-each select="//Order/lunch">
<xsl:element name="aaa">
<xsl:attribute name="uid" select="uuid:get-uuid()"/>
<discount>
<xsl:attribute name="uid" select="uuid:get-uuid()"/>
<li id="#{generate-id(drinkName)}">
<xsl:value-of select="drinkName"/>
<a href="#{generate-id(drinkPrice)}">
<xsl:value-of select="drinkPrice"/>
</a>
</li>
</discount>
</xsl:element>
</xsl:for-each>
</Payment>
</xsl:template>
<!-- Returns the UUID --><xsl:function name="uuid:get-uuid" as="xs:string*">
<xsl:variable name="ts" select="uuid:ts-to-hex(uuid:generate-timestamp())"/>
<xsl:value-of separator="-" select=" substring($ts, 8, 8), substring($ts, 4, 4), string-join((uuid:get-uuid-version(), substring($ts, 1, 3)), ''), uuid:generate-clock-id(), uuid:get-network-node()"/>
</xsl:function>
<!-- internal aux. fu with saxon, this creates a more-unique result with
generate-id then when just using a variable containing a node
--><xsl:function name="uuid:_get-node">
<xsl:comment/>
</xsl:function>
<!-- generates some kind of unique id --><xsl:function name="uuid:get-id" as="xs:string">
<xsl:sequence select="generate-id(uuid:_get-node())"/>
</xsl:function>
<!-- should return the next nr in sequence, but this can't be done
in xslt. Instead, it returns a guaranteed unique number
--><xsl:function name="uuid:next-nr" as="xs:integer">
<xsl:variable name="node">
<xsl:comment/>
</xsl:variable>
<xsl:sequence select=" xs:integer(replace( generate-id($node), '\D', ''))"/>
</xsl:function>
<!-- internal fu for returning hex digits only --><xsl:function name="uuid:_hex-only" as="xs:string">
<xsl:param name="string"/>
<xsl:param name="count"/>
<xsl:sequence select=" substring(replace( $string, '[^0-9a-fA-F]', '') , 1, $count)"/>
</xsl:function>
<!-- may as well be defined as returning the same seq each time --><xsl:variable name="_clock" select="uuid:get-id()"/>
<xsl:function name="uuid:generate-clock-id" as="xs:string">
<xsl:sequence select="uuid:_hex-only($_clock, 4)"/>
</xsl:function>
<!-- returns the network node, this one is 'random', but must
be the same within calls. The least-significant bit must be '1'
when it is not a real MAC address (in this case it is set to '1')
--><xsl:function name="uuid:get-network-node" as="xs:string">
<xsl:sequence select="uuid:_hex-only('09-17-3F-13-E4-C5', 12)"/>
</xsl:function>
<!-- returns version, for timestamp uuids, this is "1" --><xsl:function name="uuid:get-uuid-version" as="xs:string">
<xsl:sequence select="'1'"/>
</xsl:function>
<!-- Generates a timestamp of the amount of 100 nanosecond
intervals from 15 October 1582, in UTC time.
--><xsl:function name="uuid:generate-timestamp"><!-- date calculation automatically goes
correct when you add the timezone information, in this
case that is UTC.
--><xsl:variable name="duration-from-1582" as="xs:dayTimeDuration">
<xsl:sequence select=" current-dateTime() - xs:dateTime('1582-10-15T00:00:00.000Z')"/>
</xsl:variable>
<xsl:variable name="random-offset" as="xs:integer">
<xsl:sequence select="uuid:next-nr() mod 10000"/>
</xsl:variable>
<!-- do the math to get the 100 nano second intervals --><xsl:sequence select=" (days-from-duration($duration-from-1582) * 24 * 60 * 60 + hours-from-duration($duration-from-1582) * 60 * 60 + minutes-from-duration($duration-from-1582) * 60 + seconds-from-duration($duration-from-1582)) * 1000 * 10000 + $random-offset"/>
</xsl:function>
<!-- simple non-generalized function to convert from timestamp to hex --><xsl:function name="uuid:ts-to-hex">
<xsl:param name="dec-val"/>
<xsl:value-of separator="" select=" for $i in 1 to 15 return (0 to 9, tokenize('A B C D E F', ' ')) [ $dec-val idiv xs:integer(math:power(16, 15 - $i)) mod 16 + 1 ]"/>
</xsl:function>
<xsl:function name="math:power">
<xsl:param name="base"/>
<xsl:param name="power"/>
<xsl:choose>
<xsl:when test="$power lt 0 or contains(string($power), '.')">
<xsl:message terminate="yes">
The XSLT template math:power doesnt support negative or
fractional arguments.
</xsl:message>
<xsl:text>NaN</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="math:_power">
<xsl:with-param name="base" select="$base"/>
<xsl:with-param name="power" select="$power"/>
<xsl:with-param name="result" select="1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template name="math:_power">
<xsl:param name="base"/>
<xsl:param name="power"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="$power = 0">
<xsl:value-of select="$result"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="math:_power">
<xsl:with-param name="base" select="$base"/>
<xsl:with-param name="power" select="$power - 1"/>
<xsl:with-param name="result" select="$result * $base"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
答案 0 :(得分:0)
(初步不完整的回答,我稍后会回到这里)
这整个样式表非常强烈地依赖于这样的概念:当函数创建新节点时,每次调用它时它将创建不同的节点,并且不同的节点将对generate-id()具有不同的结果。也就是说,它依赖于调用函数具有微妙的副作用这一事实,结果是可以两次调用相同的函数并得到不同的结果。这当然偏离了函数式编程的常规规则,并且它对优化产生了令人讨厌的后果,因为它意味着函数调用无法从循环中拉出(如果在循环中调用f(3),则可以不要把这个电话拉出循环,只做一次。
这在https://www.w3.org/TR/xslt-30/#function-determinism
中有详细讨论XSLT 3.0允许您使用new-every-time =“yes”属性声明函数的期望。这应该是默认值,但由于Saxon在这个领域缺乏严格规则的情况下有尝试优化的历史,它可能会尝试自己评估是否需要多次调用。
运行样式表时使用-explain选项可以反馈Saxon正在应用的优化。您也可以关闭所选的优化,例如-opt:-fl关闭循环提升和函数内联。
解决整个问题的一个更好的方法是使用新的fn:random-number-generator()函数。不幸的是,这使用了更高阶的函数,因此它在Saxon-HE中不可用。