如何生成唯一字符串

7
我想将一个XML文档转换成HTML。一些XML元素具有指向其他文档的链接,例如:
<link href="1.html">

在HTML输出中,我希望得到以下内容:
<a href="1.html&no_cache={unique_id}">

如何生成一个较大的唯一ID?

generate-id() - khachik
1
在我的情况下不适用。如果我尝试多次处理这个 XML,generate-id() 就无法生成唯一的 ID。我想每次都有唯一的 ID。 - Nawa
好问题,加一。看看我的答案,里面有一个完整又简短的XSLT解决方案。 :) - Dimitre Novatchev
请注意,所有回答说“这不能用XSLT完成”的都是错误的。 :) - Dimitre Novatchev
4个回答

6
首先,我假设由于某种未知原因,您无法在链接中使用绝对URL作为所需的UID -- 这是最简单和最自然的解决方案。
如果我的假设是正确的,那么:
这对于XSLT来说是一项容易的任务。
因为OP希望在多次执行转换时生成的ID相同,所以不适合使用generate-id()函数。
下面是一种产生稳定ID的简单方法:
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="link[@href]">
  <xsl:variable name="vUid">
    <xsl:number level="any" count="link[@href]"/>
  </xsl:variable>
   <a href="{@href}&amp;no_cache={{{$vUid}}}"/>
 </xsl:template>
</xsl:stylesheet>

当应用此转换到以下 XML 文档时(无论应用多少次):

<t>
 <link href="1.html"/>
 <a>
   <link href="2.html"/>
  <b>
    <link href="3.html"/>
    <c>
     <link href="4.html"/>
    </c>
    <link href="5.html"/>
  </b>
  <link href="6.html"/>
  <d>
   <link href="7.html"/>
  </d>
 </a>
 <link href="8.html"/>
 <e>
  <link href="9.html"/>
 </e>
 <link href="10.html"/>
</t>

每次都产生期望的、相同的、正确的结果:

<t>
   <a href="1.html&amp;no_cache={1}"/>
   <a>
      <a href="2.html&amp;no_cache={2}"/>
      <b>
         <a href="3.html&amp;no_cache={3}"/>
         <c>
            <a href="4.html&amp;no_cache={4}"/>
         </c>
         <a href="5.html&amp;no_cache={5}"/>
      </b>
      <a href="6.html&amp;no_cache={6}"/>
      <d>
         <a href="7.html&amp;no_cache={7}"/>
      </d>
   </a>
   <a href="8.html&amp;no_cache={8}"/>
   <e>
      <a href="9.html&amp;no_cache={9}"/>
   </e>
   <a href="10.html&amp;no_cache={10}"/>
</t>

请注意: 使用<xsl:number>来生成id。

如果同一个链接在文档中出现多次,我们需要所有出现的地方使用相同的id,这是解决此问题的方法:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kHrefByVal" match="link/@href" use="."/>

 <xsl:variable name="vUniqHrefs" select=
  "//link/@href
       [generate-id()
       =
        generate-id(key('kHrefByVal',.)[1])
       ]
  "/>


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

 <xsl:template match="link[@href]">
  <xsl:variable name="vthisHref" select="@href"/>

  <xsl:variable name="vUid">
   <xsl:for-each select="$vUniqHrefs">
    <xsl:if test=". = $vthisHref">
     <xsl:value-of select="position()"/>
    </xsl:if>
   </xsl:for-each>
  </xsl:variable>
   <a href="{@href}&amp;no_cache={{{$vUid}}}"/>
 </xsl:template>
</xsl:stylesheet>

当对以下XML文档应用此转换时:

<t>
 <link href="1.html"/>
 <a>
   <link href="2.html"/>
  <b>
    <link href="1.html"/>
    <c>
     <link href="3.html"/>
    </c>
    <link href="2.html"/>
  </b>
  <link href="1.html"/>
  <d>
   <link href="3.html"/>
  </d>
 </a>
 <link href="4.html"/>
 <e>
  <link href="2.html"/>
 </e>
 <link href="4.html"/>
</t>

期望获得的正确结果已经生成:

<t>
   <a href="1.html&amp;no_cache={1}"/>
   <a>
      <a href="2.html&amp;no_cache={2}"/>
      <b>
         <a href="1.html&amp;no_cache={1}"/>
         <c>
            <a href="3.html&amp;no_cache={3}"/>
         </c>
         <a href="2.html&amp;no_cache={2}"/>
      </b>
      <a href="1.html&amp;no_cache={1}"/>
      <d>
         <a href="3.html&amp;no_cache={3}"/>
      </d>
   </a>
   <a href="4.html&amp;no_cache={4}"/>
   <e>
      <a href="2.html&amp;no_cache={2}"/>
   </e>
   <a href="4.html&amp;no_cache={4}"/>
</t>

+1 很好的回答。在第二个问题中,你可以使用<xsl:for-each select="key('kHrefByVal',@href)[1]/.."><xsl:number level="any"/></xsl:for-each>来提高性能。 - user357812

3

generate-id(.)在我尝试多次处理此XML时不会生成唯一的ID。我希望每次都有唯一的ID。 - Nawa
@Nawa:generate-id()函数可以在同一转换期间为输入源中的每个节点提供唯一标识符。如果您需要一个永久唯一标识符,则需要实现一些像MD5这样的算法。 - user357812
你好,nawa。每次调用转换时都应该生成新的ID,但目前还没有关于这些ID生成的标准。W3C只是说此ID在当前转换中的所有节点上是唯一的。对于真正唯一的ID,你最好使用类似于从XSL转换内调用.net方法并从.net程序集中返回Guid.NewGuid()的方法。或者,只需进行转换并使用#UNIQUEID#(静态文本),然后在每个出现的#UNIQUEID#之后使用你选择的语言替换为唯一标识符。 - Stephan Schinkel

2

使用纯XSLT不可能实现这一点,但有一些替代选项:

  1. 添加扩展命名空间,以便调用非XSLT代码:<a href="1.html&no_cache={myns:unique_id()}">。这将给您想要的结果,但取决于您用于执行转换的框架是否支持。
  2. 使用JavaScript在客户端上添加唯一ID到链接中。仅在客户端启用了JavaScript时才有效,但如果您知道这种情况将发生,则可能是可接受的妥协。
  3. 设置页面的HTTP标头以防止缓存。从语义角度来看,这可能是最好的选择,而且您不会冒每个唯一ID都被搜索引擎重复爬行的风险。

1
好的建议,虽然“使用纯 XSLT 不可能”并不正确。 - LarsH

0

XSLT是一种函数语言,这意味着对于给定的输入,它将始终产生相同的输出,因此按定义,GUID方法或任何其他随机生成器都不会成为设计规范的一部分。如果您的客户有要求,最好使用与时间相关的方法作为伪随机种子的一部分来生成ID,但是由于您的目标似乎是强大的反缓存,您应该放弃这一点,并专注于将正确的反缓存头应用于您试图保护的资源。


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