XSLT生成UUID

16

如何使用纯XSLT生成UUID?基本上是寻找一种使用XSLT创建唯一序列的方法。 序列可以是任意长度。

我正在使用XSLT 2.0。


可能是在XSLT中生成GUID的重复问题。 - Wayne
7个回答

16

这里有一个好例子。基本上,你需要设置一个指向java UUID类的扩展,并在XSL中引用它:

<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:uuid="java:java.util.UUID">
<xsl:template match="/">
  <xsl:variable name="uid" select="uuid:randomUUID()"/>
  <xsl:value-of select="$uid"/>
</xsl:template>

抱歉,之前没有澄清。我确实看过那个例子,但需要使用纯XSLT完成,不能用Java。 - Ayyoudy
1
我已经点赞,因为这对我很有用。然而,值得注意的是,像这样的反射Java调用在Saxon HE许可证中也不可用(尽管正如其他人所指出的那样,编写自己的函数来执行它更或多或少是一个解决的问题)。 - Tom Hillman
请忽略xsl:stylesheet/xsl:value-of无效的问题,甚至没有关闭的xsl:stylesheet标签,它都不是格式良好的;)虽然我相信我们都可以理解意思! - Tom Hillman

7
您可以使用xslt片段实现此功能(来源:http://code.google.com/p/public-contracts-ontology/source/browse/transformers/GB-notices/uuid.xslt?r=66e1d39a1c140079a86d219df5b3e031007cc957):
<xsl:stylesheet xmlns:uuid="http://www.uuid.org" xmlns:math="http://exslt.org/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

        <xsl:template match="/">
                <xsl:value-of select="
concat('First random ID:', uuid:get-id()),
concat('Base timestamp: ', uuid:generate-timestamp()),
concat('Clock id: ' ,uuid:generate-clock-id()),
concat('Network node: ' ,uuid:get-network-node()),
concat('UUID Version: ' ,uuid:get-uuid-version()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid())
" separator="&#10;"/>
        </xsl:template>

    <!--
Functions in the uuid: namespace are used to calculate a UUID
The method used is a derived timestamp method, which is explained
here: http://www.famkruithof.net/guid-uuid-timebased.html
and here: http://www.ietf.org/rfc/rfc4122.txt
-->
    <!--
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 doesn't 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>
</xsl:stylesheet>:choose>
    </xsl:template>
</xsl:stylesheet>

1
谢谢分享。源代码(public-contracts-ontology)是一个失效的链接 :( - Paul Bußmann
1
使用此代码可以获取4个不同的UUID: "<xsl:value-of select="concat(' ', uuid:get-uuid()), concat(' ', uuid:get-uuid())" /><xsl:value-of select="concat(' ', uuid:get-uuid()), concat(' ', uuid:get-uuid())" />": 23CD00A6-8952-11E6-2114-09173F13E4C5 23CD00A5-8952-11E6-2114-09173F13E4C5 23CD00A3-8952-11E6-2114-09173F13E4C5 23CD00A4-8952-11E6-2114-09173F13E4C5 - Paul Bußmann
但是使用"<xsl:for-each ...",它总是以相同的UUID开头。例如:"<xsl:for-each select="Test"><xsl:value-of ... /></xsl:for-each>": 382967A6-8952-11E6-2114-09173F13E4C5 382967A5-8952-11E6-2114-09173F13E4C5 382967A3-8952-11E6-2114-09173F13E4C5 382967A4-8952-11E6-2114-09173F13E4C5 382967A6-8952-11E6-2114-09173F13E4C5 382967A5-8952-11E6-2114-09173F13E4C5 382967A3-8952-11E6-2114-09173F13E4C5 382967A4-8952-11E6-2114-09173F13E4C5有没有办法在"xls:for-each"中获取新的UUID? - Paul Bußmann
1
使用 <xsl:for-each select="uuid:get-uuid()"><xsl:attribute name="Test" select="."/></xsl:for-each> 在 for-each 循环中获取唯一值。 - Tyler Dickson

5

以下是使用纯XPath 2或更高版本进行此操作的最简单和最短的方法:

unparsed-text("https://uuidgen.org/api/v/4")

如果您想生成多个GUID,比如100个,请使用以下方法:
for $i in 1 to 100
  return unparsed-text(concat("https://uuidgen.org/api/v/4?x=", $i))
解释
由于标准的XSLT 2.0 / XPath 3.0 (以及更高版本)函数 unparsed-text() 确定性的,因此它必须对相同的参数在每次调用时返回相同的结果。遵循规范的XPath实现必须缓存第一次响应,并且仅为相同的参数产生缓存响应。
为了避免这种情况,我们为每个调用生成一个略微不同的URL,从而使XPath规范作者的努力徒劳无功。
享受 enter image description here

@user:4099593:请恢复另一个答案并删除这个。我们能否在这里至少提供一个链接到已恢复的答案?这对读者会很有帮助。感谢您的理解。 - Dimitre Novatchev
@bhargav-rao,请恢复另一个答案并删除此答案。我们能否在这里至少提供一个链接到已恢复的答案?这对读者会很有帮助。感谢您的理解。 - Dimitre Novatchev

4

由于XSLT是一种函数式语言,因此生成随机数不是该语言的一部分。 话虽如此,有一些扩展包(EXSLT)和一些处理器(Saxon)支持生成随机数。 如果您无法使用扩展或Saxon,则我相信您就没有办法了。


2
Saxon如何支持生成随机数? - Ayyoudy
Saxon自带EXSLT随机模块。请参阅http://saxonica.com/documentation/extensions/intro.xml。 - Jim Garrison
谢谢。很遗憾,Saxon HE(Home Edition)没有内置的EXSLT模块可用。 - Ayyoudy
并不是真的没有希望...只是不太方便。大多数随机数生成器都是用软件编写的。因此,如果CPU性能不是瓶颈,您可以在纯标准XSLT中实现标准的随机数生成算法。 - LarsH
2
Braaksma的基于时间戳的实现(http://www.stylusstudio.com/xsllist/200703/post40430.html)仅使用了一个扩展函数`math:power()`。而`math:power()`是使用纯XSLT实现的(作为模板),在这里:http://www.exslt.org/math/functions/power/math.power.template.xsl - LarsH
显示剩余3条评论

4

3

要在XSLT中生成随机数,请参考使用FXSL掷骰子:XSLT中的随机数生成函数。它使用的唯一扩展函数是node-set(),但在XSLT 2.0中不再需要。

此外,如果要求仅为ID唯一(不一定随机),请查看如何生成唯一字符串。例如,如果您为输入XML文档的每个元素生成UUID,则可以使用输入文档的URL和的组合来为每个元素生成唯一字符串。


1
如果使用.Net的XslCompiledTransform来转换您的XSL,您可以将EnableScripts属性设置为true,然后使用以下代码:
<msxsl:script language="C#" implements-prefix="csharp">
    <![CDATA[
    public static string NewGuid()
    {
        return Guid.NewGuid().ToString();
    }
    ]]>
</msxsl:script>

注意:我在上面给这个自定义功能命名/前缀为csharp,但你可以随意更改。

有关启用脚本的更多信息,请参见https://dev59.com/7ErSa4cB1Zd3GeqPYrrJ#1873265

以下是完整的XSLT文件,以提供一些额外的上下文:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:csharp="urn:JohnLBevan/NewGuid"
    exclude-result-prefixes="xsl msxsl csharp"
>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="//*/text()">
        <!-- replaces all text nodes from input document with GUIDs -->
        <xsl:value-of select="csharp:NewGuid()"/>
    </xsl:template>

    <msxsl:script language="C#" implements-prefix="csharp">
        <![CDATA[
        public static string NewGuid()
        {
            return Guid.NewGuid().ToString();
        }
        ]]>
    </msxsl:script>

</xsl:stylesheet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接