使用XSLT 1.0将字符串限制为列入白名单的字符

时间:2008-09-25 05:38:16

标签: string xslt whitelist

问题

使用XSLT 1.0,给定一个包含任意字符的字符串,如何找回符合以下规则的字符串。

  1. 第一个字符必须是以下字符之一:a-z,A-Z,冒号或下划线
  2. 所有其他字符必须是以上任何字符或0-9,句号或连字符
  3. 如果任何字符不符合上述规则,请将其替换为下划线
  4. 背景

    在XSLT中我将一些属性转换为元素,但我需要确保该属性不包含任何不能在元素名称中使用的值。我不关心转换为名称的属性的完整性,只要它被可预测地转换。我也不需要在元素名称中补充每个有效字符(有一堆)。

    我遇到的问题是有空格的属性,翻译功能可以轻松转换为下划线:

    translate(@name,' ','_')
    

    但是在我发现一些使用斜杠的属性之后不久,我现在也必须添加它。这很快就会失控。我希望能够定义允许字符的白名单,并用下划线替换任何不允许的字符,但翻译可以通过替换黑名单来实现。

3 个答案:

答案 0 :(得分:6)

可以编写一个递归模板来执行此操作,逐个处理字符串中的字符,测试它们并在必要时更改它们。类似的东西:

<xsl:template name="normalizeName">
  <xsl:param name="name" />
  <xsl:param name="isFirst" select="true()" />
  <xsl:if test="$name != ''">
    <xsl:variable name="first" select="substring($name, 1, 1)" />
    <xsl:variable name="rest" select="substring($name, 2)" />
    <xsl:choose>
      <xsl:when test="contains('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_', $first) or
                      (not($first) and contains('0123456789.-', $first))">
        <xsl:value-of select="$first" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>_</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:call-template name="normalizeName">
      <xsl:with-param name="name" select="$rest" />
      <xsl:with-param name="isFirst" select="false()" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

然而,如果你准备好了一些hackery,那么这样做的方法会更短。首先声明一些变量:

<xsl:variable name="underscores"
  select="'_______________________________________________________'" />
<xsl:variable name="initialNameChars"
  select="'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_'" />
<xsl:variable name="nameChars"
  select="concat($initialNameChars, '0123456789.-')" />

现在的技巧是取名称并通过将 合法的名称中的所有字符替换为任何内容来识别合法的字符。您可以使用translate()功能执行此操作。一旦你获得了字符串中出现的非法字符集,就可以再次使用translate()函数用下划线替换它们。这是模板:

<xsl:template name="normalizeName">
  <xsl:param name="name" />
  <xsl:variable name="first" select="substring($name, 1, 1)" />
  <xsl:variable name="rest" select="substring($name, 2)" />
  <xsl:variable name="illegalFirst"
    select="translate($first, $initialNameChars, '')" />
  <xsl:variable name="illegalRest"
    select="translate($rest, $nameChars, '')" />
  <xsl:value-of select="concat(translate($first, $illegalFirst, $underscores),
                               translate($rest, $illegalRest, $underscores))" />
</xsl:template>

唯一需要注意的是,下划线字符串需要足够长以覆盖可能出现在单个名称中的所有非法字符。使它与您可能遇到的最长名称相同的长度将起到作用(尽管可能你可以通过缩短它来缩短它)。

答案 1 :(得分:1)

据我所知,XSLT 1.0没有内置功能。 XSLT 2.0允许你use regexes,但我确信你都清楚这一点。

如果您使用MS解析器,您可以编写可以在XSLT中使用的.NET扩展库,并且我写了some months ago here.

如果你使用像Saxon这样的东西,我很确定他们也提供了编码你自己的扩展的方法,他们可能确实已经扩展了自己的扩展,但我不熟悉那个引擎。

希望这有帮助。

答案 2 :(得分:0)

作为另一种选择,有一个字符串函数可能适用于XSLT标准库。 http://xsltsl.sourceforge.net/string.html#template.str:string-match