在XSLT 1.0中生成UUID

3
在我们的遗留项目中,我们正在使用基于 xslt 1.0 版本的 libxslt。现在需要使用 xslt 文件生成 UUID,以便我们的输出 xml 文件包含 UUID。
根据 https://dev59.com/mWsz5IYBdhLWcg3wFT4f#8127174,我运气不好。
另外根据 https://gist.github.com/azinneera/778f69ae6b0049b5edcd69da70072405,我们可以生成 UUID,但需要使用 xslt 2.0。

我对xslt很陌生,有没有办法将版本2.0的样式表https://gist.github.com/azinneera/778f69ae6b0049b5edcd69da70072405转换为1.0,或者有没有其他方法使用xslt 1.0生成UUID?


看看这个是否有帮助;https://stackoverflow.com/a/25869149/3016153 - michael.hor257k
我运气不好。不完全正确。如果您所说的UUID是RFC4122或ITU-T Rec. X.667,则XSLT中没有内置支持。扩展XSLT功能的众所周知机制是使用扩展函数。几乎每个XSLT处理器都公开了API,允许扩展函数注册。使用您选择的可信UUID库进行注册。 - Alejandro
本文介绍如何生成随机数,并类似于xslt 1.0。但我认为你应该已经看过了,有什么问题吗?http://fxsl.sourceforge.net/articles/Random/Casting%20the%20Dice%20with%20FXSL-htm.htm - Sohail
@Sohail 这会生成随机数,我需要基于 https://github.com/wmo-im/iwxxm/issues/31#issuecomment-342307281 生成UUID v4。 - NJMR
1
@NJMR 在XSLT 1.0中无法生成UUID v4。因为XSLT 1.0无法生成随机数,而规范指出:“版本4 UUID用于从真正随机或伪随机数生成UUID。”如果您正在使用libxslt,则可以使用EXSLT math:random() 扩展函数生成随机数,并按照规范第4.4节所述继续进行。请注意,您要转换的样式表旨在生成基于当前时间戳的版本1 UUID。 - michael.hor257k
设置带有外部库(boost/uuid 可行,但 libxslt 较为复杂)的 C++ 环境似乎太麻烦了。但我将通过这个Python示例向您展示扩展函数方法有多么简单。请注意,Python在幕后使用libxslt,因此API几乎相同。 - Alejandro
2个回答

2

如我在您的问题评论中所述,如果您使用libxslt处理器,则可以使用EXSLT math:random()扩展函数生成一系列随机数,最终形成版本4 UUID。

以下是一个实现示例:

XSLT 1.0 + EXSLT

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
xmlns:func="http://exslt.org/functions"
xmlns:my="www.example.com/my"
extension-element-prefixes="func math my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<func:function name="my:UUID4">
    <!-- https://www.ietf.org/rfc/rfc4122.txt -->
    <func:result>
        <!-- 8 -->
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:text>-</xsl:text>      
        <!-- 4 -->
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <!-- version identifier -->
        <xsl:text>-4</xsl:text>     
        <!-- 3 -->
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:text>-</xsl:text>      
        <!-- 1* -->
        <xsl:value-of select="substring('89ab', floor(4*math:random()) + 1, 1)" />
        <!-- 3 -->
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:text>-</xsl:text>      
        <!-- 12 -->
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" />
        <xsl:value-of select="substring('0123456789abcdef', floor(16*math:random()) + 1, 1)" /> 
    </func:result>
</func:function>

<xsl:template match="/items">
    <output>
        <xsl:for-each select="item">
            <item id="{my:UUID4()}">
                <xsl:value-of select="." />
            </item>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

当应用于以下输入时:

XML

<items>
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <item>4</item>
    <item>6</item>
    <item>7</item>
    <item>8</item>
    <item>9</item>
</items>

我得到了以下结果:

结果1

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <item id="77587d4c-1ef6-4aaf-9f97-398dee70fa25">1</item>
  <item id="148e4218-c881-41d3-af61-cab4b5d0251f">2</item>
  <item id="3a02b568-3200-46ff-993c-3bea9724d6ce">3</item>
  <item id="28de29bd-39f4-4eed-979a-765c290652a1">4</item>
  <item id="7c767fa7-c0b7-4187-9f86-d3876ec1be8a">6</item>
  <item id="aca2261f-e837-4a2d-a555-0c81b2c7f7a2">7</item>
  <item id="b7ecb7bd-8c3e-475d-ba17-4c62c1c3d90b">8</item>
  <item id="d28f95e8-452c-474f-9c9a-11e09cd948ae">9</item>
</output>

后续运行产生了:

结果2

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <item id="6eb63a8e-599d-450a-8970-a758b73aa121">1</item>
  <item id="86b247bf-81c8-47ce-9375-4a35e44fcde7">2</item>
  <item id="cbc04786-9e90-4331-a9d3-47955c7d5a99">3</item>
  <item id="9f82f8d0-9934-499e-8783-61087ebce2f7">4</item>
  <item id="5b77da5b-f28f-45a7-82f4-a47b6b1aa7b2">6</item>
  <item id="7eab11bc-209f-4100-b4e6-1cc0f73beda0">7</item>
  <item id="7f4151f4-6166-4406-9ee4-e7de325537d0">8</item>
  <item id="2185c4b8-6a74-4b97-93b4-872b2c0e1f5e">9</item>
</output>

结果3

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <item id="784b9cd0-a77a-4719-ad0b-183a970b6785">1</item>
  <item id="4dbed80b-4c82-4dde-8a0a-8b29471bdbbf">2</item>
  <item id="0297ad52-3070-4b6a-a28b-a9c7c4607027">3</item>
  <item id="8e402219-3fbf-4025-827b-c95ae4e12ea0">4</item>
  <item id="140c8fad-2d93-4b77-b548-5a150f350d81">6</item>
  <item id="5ca365ac-43dd-41fa-9fa7-6237971576aa">7</item>
  <item id="6ac7bb94-88cd-442e-8c3b-933ca3d53fb5">8</item>
  <item id="3cc77134-77ee-4405-bf33-92e6dc7bfdc1">9</item>
</output>

等等。


@michael.hor257k:有时候我会得到长度较短的UUID... - NJMR
我会对上面的解决方案保持谨慎!我在我的用例中使用和创建了大量的UUID。最终,我们发现这会导致很多重复的UUID。 - BernhardS
@BernhardS,“巨大的数字”是什么意思?“很多”指多少? - michael.hor257k
@michael.hor257k,我生成了5974983个UUID。我没有计算重复的数量,但是有几个,因为我们在几分钟内通过手动搜索找到了2个。因此,由于组合的最大数量为2¹²²,这使我得出结论,问题基于任何类型的math:random。 也许明天我可以再次复现它,以便告诉您确切的重复次数,但现在我们已经通过一个节点脚本(randomUUID)解决了它。 - BernhardS
1
@BernhardS 我刚刚生成了1百万个UUID,分成了100批,每批10,000个。其中有整整96%的重复。但问题在于:这些重复都来自两个毗邻批次完全相同的UUID。我的结论是随机函数的种子在调用之间没有改变 - 最可能的情况是它只在每秒更新一次。我在调用之间进行了3秒的暂停,再次测试时没有重复。 - michael.hor257k
显示剩余5条评论

1

有一些基于语言和方法的解决方案,适用于版本 1.0 的 XSLT。

让我们看下面的示例 XML。(示例 XML 取自 https://www.cs.utexas.edu/~mitra/csFall2015/cs329/lectures/xml/xslplanes.2.xml.txt

<?xml version = "1.0" encoding = "utf-8"?>
<planes xmlns="planes_from_cs_utexas_edu">
   <plane>
      <year> 1977 </year>
      <make> Cessna </make>
      <model> Skyhawk </model>
      <color> Light blue and white </color>
   </plane>
   <plane>
      <year> 1975 </year>
      <make> Piper </make>
      <model> Apache </model>
      <color> White </color>
   </plane>   
   <plane>
      <year> 1960 </year>
      <make> Cessna </make>
      <model> Centurian </model>
      <color> Yellow and white </color>
   </plane>
   <plane>
      <year> 1956 </year>
      <make> Piper </make>
      <model> Tripacer </model>
      <color> Blue </color>
   </plane>
</planes>

由于问题需要使用C++,因此有以下解决方案。

1. 使用Xalan C++版本(似乎适合该问题

https://xalan.apache.org/old/xalan-c/extensions.html中有一个C++示例代码。它仅显示平方根,但可以转换为创建GUID,例如在Windows中使用CoCreateGuid()方法或在Linux环境中使用libuuid,并将其转换为XObjectPtr作为将GUID转换为XalanDOMString。


例如,如果使用其他语言,则可以采用以下解决方案。

Java/.NET(以下示例为Java,但这些方法也适用于任何.NET语言)
1. 使用反射扩展函数(基于Saxon)
注意:此解决方案仅适用于Saxon-PE和Saxon-EE

XSLT可以如下所示,包括直接调用Java的UUID类的方法。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:uuid="java:java.util.UUID"
xmlns:ns1="planes_from_cs_utexas_edu" 
exclude-result-prefixes="uuid">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/ns1:planes">
     <planes>
         <xsl:for-each select = "ns1:plane">
          <plane>
            <year>
                <xsl:value-of select="ns1:year" />
            </year>
            <make>
                <xsl:value-of select="ns1:make" />
            </make>
            <model>
                <xsl:value-of select="ns1:model" />
            </model>
            <color>
                <xsl:value-of select="ns1:color" />
            </color>
            <uuid>
                <xsl:value-of select="uuid:randomUUID()"/>
            </uuid>
       </plane>
       </xsl:for-each>
     </planes>
    </xsl:template>
</xsl:stylesheet>

输出结果将是:

<?xml version="1.0" encoding="utf-8"?>
<planes xmlns:ns1="planes_from_cs_utexas_edu">
   <plane>
      <year> 1977 </year>
      <make> Cessna </make>
      <model> Skyhawk </model>
      <color> Light blue and white </color>
      <uuid>50ef735f-a1a1-46cb-a638-05966b2c2b78</uuid>
   </plane>
   <plane>
      <year> 1975 </year>
      <make> Piper </make>
      <model> Apache </model>
      <color> White </color>
      <uuid>8e9b5345-445c-4700-8191-08731c44e1e0</uuid>
   </plane>
   <plane>
      <year> 1960 </year>
      <make> Cessna </make>
      <model> Centurian </model>
      <color> Yellow and white </color>
      <uuid>01b01db9-982a-4811-a5b3-efa73a39dacd</uuid>
   </plane>
   <plane>
      <year> 1956 </year>
      <make> Piper </make>
      <model> Tripacer </model>
      <color> Blue </color>
      <uuid>3a2f7ee2-c53c-46b5-903f-39a21990aa75</uuid>
   </plane>
</planes>

2. 使用集成扩展函数(基于Saxon)
注意:此解决方案适用于所有Saxon版本

请参见http://saxonica.com/html/documentation/extensibility/integratedfunctions/
另外,在Saxon-HE Integrated Extension Functions | how and where?中有一个示例用法。


C#

1. 使用msxsl:script编写XSLT样式表脚本(基于Microsoft处理器)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="planes_from_cs_utexas_edu" 
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="urn:my-scripts"
exclude-result-prefixes="uuid">
<msxsl:script language="C#" implements-prefix="user">  
     <![CDATA[  
     public double uuid()  
     {  
       return Guid.NewGuid().ToString(); 
     }  
      ]]>  
 </msxsl:script> 

<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/ns1:planes">
     <planes>
         <xsl:for-each select = "ns1:plane">
          <plane>
            <xsl:copy-of select="node()"/>
            <uuid>
                <xsl:value-of select="user:uuid()"/>
            </uuid>
       </plane>
       </xsl:for-each>
     </planes>
    </xsl:template>
</xsl:stylesheet>

输出结果将类似于上面的示例输出。

参考资料:https://learn.microsoft.com/en-us/dotnet/standard/data/xml/xslt-stylesheet-scripting-using-msxsl-script


请注意,msxsl-script 不是注册扩展函数的标准机制。此外,该问题涉及到 libxslt,正确的文档在 http://xmlsoft.org/libxslt/extensions.html#Registerin1 中。 - Alejandro

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