Использование группировки для объединения текста и последующего его тестирования

Так что в этом отвратительном экструдированном наборном продукте я иногда вижу ссылки и адреса электронной почты, которые были разделены на части. Пример:

<p>Here is some random text with an email address 
<Link>example</Link><Link>@example.com</Link> and here 
is more random text with a url 
<Link>http://www.</Link><Link>example.com</Link> near the end of the sentence.</p>

Желаемый результат:

<p>Here is some random text with an email address 
<email>[email protected]</email> and here is more random text 
with a url <ext-link ext-link-type="uri" xlink:href="http://www.example.com/">
http://www.example.com/</ext-link> near the end of the sentence.</p>

Пробелы между элементами, кажется, не возникают, что является одним из благословений.

Я могу сказать, что мне нужно использовать xsl:for-each-group в шаблоне p, но я не совсем понимаю, как поместить объединенный текст из группы через функцию contains(), чтобы отличить электронные письма от URL-адресов. Помощь?


person D.Salo    schedule 03.02.2010    source источник
comment
Как вы решаете, что представляет собой отдельный элемент ссылки? Всегда ли есть только два смежных элемента «Ссылка» или может быть больше смежных элементов «Ссылка», которые могут даже принадлежать более чем одному отдельному элементу ссылки? Является ли ‹Link›example‹/Link›‹Link›@example.com‹/Link›‹Link›‹/Link›‹Link›example.com‹/Link› можно?   -  person Martin Honnen    schedule 04.02.2010
comment
На данный момент я не сталкивался с более чем двумя последующими, но больше кажется возможным. Я не думаю, что две ссылки подряд вероятны (надеюсь, что нет!).   -  person D.Salo    schedule 23.02.2010


Ответы (2)


Если вы используете group-adjacent, вы можете просто присоединиться к текущей группе (), как в

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xsd"
  version="2.0">

  <xsl:template match="p">
    <xsl:copy>
      <xsl:for-each-group select="node()" group-adjacent="boolean(self::Link)">
        <xsl:choose>
          <xsl:when test="current-grouping-key()">
            <xsl:variable name="link-text" as="xsd:string" select="string-join(current-group(), '')"/>
            <xsl:choose>
              <xsl:when test="matches($link-text, '^https?://')">
                <ext-link ext-link-type="uri" xlink:href="{$link-text}">
                  <xsl:value-of select="$link-text"/>
                </ext-link>
              </xsl:when>
              <xsl:otherwise>
                <email><xsl:value-of select="$link-text"/></email>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="current-group()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
person Martin Honnen    schedule 04.02.2010

Вот решение XSLT 1.0, основанное на шаблоне удостоверения, со специальной обработкой элементов <Link>.

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

<xsl:template match="Link">
  <xsl:if test="not(preceding-sibling::node()[1][self::Link])">
    <xsl:variable name="link">
      <xsl:copy-of select="
        text()
        | 
        following-sibling::Link[
          preceding-sibling::node()[1][self::Link]
          and
          generate-id(current())
          =
          generate-id(
            preceding-sibling::Link[
              not(preceding-sibling::node()[1][self::Link])
            ][1]
          )
        ]/text()
      " />
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="contains($link, '://')">
        <ext-link ext-link-type="uri" xlink:href="{$link}" />
      </xsl:when>
      <xsl:when test="contains($link, '@')">
        <email>
          <xsl:value-of select="$link" />
        </email>
      </xsl:when>
      <xsl:otherwise>
        <link type="unknown">
          <xsl:value-of select="$link" />
        </link>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>
</xsl:template>

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

not(preceding-sibling::node()[1][self::Link])

означает, что «непосредственно предшествующий узел не должен быть <Link>», например: только <Link> элементов, которые являются «первыми в строке».

following-sibling::Link[
  preceding-sibling::node()[1][self::Link]
  and
  generate-id(current())
  =
  generate-id(
    preceding-sibling::Link[
      not(preceding-sibling::node()[1][self::Link])
    ][1]
  )
]

означает

  • from all following-sibling <Link>s, choose the ones that
    • immediately follow a <Link> (e.g. they are not "first in a row"), and
    • идентификатор узла current() (всегда <Link>, который является «первым в строке») должен быть равен:
    • ближайший предшествующий <Link>, который сам является "первым в строке"

Если это имеет смысл.

Применительно к вашему вводу я получаю:

<p>Here is some random text with an email address
<email>[email protected]</email> and here
is more random text with a url
<ext-link ext-link-type="uri" xlink:href="http://www.example.com" /> near the end of the sentence.</p>
person Tomalak    schedule 03.02.2010