Ограничить строку символами из белого списка, используя XSLT 1.0 - PullRequest
0 голосов
/ 25 сентября 2008

Вопрос

Используя XSLT 1.0, учитывая строку с произвольными символами, как я могу получить строку, соответствующую следующим правилам.

  1. Первый символ должен быть одним из следующих: a-z, A-Z, двоеточие или подчеркивание
  2. Все остальные символы должны быть любыми из указанных выше или 0-9, точка или дефис
  3. Если какой-либо символ не соответствует вышеуказанным правилам, замените его подчеркиванием

Фон

В XSLT я перевожу некоторые атрибуты в элементы, но мне нужно убедиться, что атрибут не содержит значений, которые нельзя использовать в имени элемента. Меня не волнует целостность атрибута, преобразуемого в имя, если он конвертируется предсказуемо. Мне также не нужно компенсировать каждый действительный символ в имени элемента (есть куча).

Проблема, с которой я столкнулся, заключалась в атрибутах с пробелами, которые функция перевода может легко преобразовать в подчеркивание:

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

Но вскоре после того, как я нашел некоторые атрибуты, используя косую черту, я должен добавить это и сейчас. Это быстро выйдет из-под контроля. Я хочу иметь возможность определять белый список разрешенных символов и заменять любые недопустимые символы подчеркиванием, но перевод работает как замена из черного списка.

Ответы [ 3 ]

6 голосов
/ 25 сентября 2008

Вы могли бы написать рекурсивный шаблон, чтобы сделать это, обрабатывая символы в строке один за другим, проверяя их и изменяя их при необходимости. Что-то вроде:

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

Единственное, на что вам следует обратить внимание, это на то, что строка подчеркивания должна быть достаточно длинной, чтобы охватить все недопустимые символы, которые могут появляться в одном имени. Его длина будет соответствовать длине самого длинного имени, с которым вы, вероятно, столкнетесь, и поможет вам (хотя, возможно, вам это и сойдет с рук, намного короче).

1 голос
/ 25 сентября 2008

Насколько я знаю, XSLT 1.0 не имеет встроенной функции для этого. XSLT 2.0 позволяет вам использовать регулярные выражения , хотя я уверен, что вы слишком об этом знаете.

Если вы случайно используете парсер MS, вы можете написать библиотеки расширений .NET, которые вы можете использовать в своем XSLT, и я писал об этом несколько месяцев назад здесь.

Если вы используете что-то вроде Saxon, я вполне уверен, что они также предоставляют способы кодирования ваших собственных расширений, и у них действительно может быть собственное расширение, но я не знаком с этим механизмом.

Надеюсь, это поможет.

0 голосов
/ 26 сентября 2008

В качестве другой альтернативы есть строковая функция, которая может работать для вас в стандартной библиотеке XSLT. http://xsltsl.sourceforge.net/string.html#template.str:string-match

...