0

问题

使用 XSLT 1.0,给定一个带有任意字符的字符串,我如何才能取回符合以下规则的字符串。

  1. 第一个字符必须是以下之一:az、AZ、冒号或下划线
  2. 所有其他字符必须是上述任何字符或 0-9、句点或连字符
  3. 如果任何字符不符合上述规则,请用下划线替换

背景

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

我遇到的问题是带有空格的属性,翻译函数可以轻松地将其转换为下划线:

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

但是不久之后我发现了一些使用斜线的属性,所以我现在也必须添加它。这将很快失控。我希望能够定义允许字符的白名单,并用下划线替换任何不允许的字符,但翻译的工作原理是从黑名单中替换。

4

3 回答 3

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>

但是,如果您准备好进行一些黑客攻击,则可以使用更短的方法来执行此操作。首先声明一些变量:

<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>

唯一需要注意的是,下划线字符串需要足够长,以覆盖单个名称中可能出现的所有非法字符。使它的长度与您可能遇到的最长名称的长度相同就可以解决问题(尽管您可能会逃脱它的长度要短得多)。

于 2008-09-25T09:01:44.360 回答
1

据我所知,XSLT 1.0 没有为此内置的。XSLT 2.0 允许您使用 regexes,尽管我确信您非常清楚这一点。

如果您使用 MS 解析器的机会不大,您可以编写可以在 XSLT 中利用的 .NET 扩展库,几个月前我在这里写过这个。

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

希望这可以帮助。

于 2008-09-25T06:10:26.660 回答
0

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

于 2008-09-25T22:24:06.600 回答