Обтекание слов из HTML с использованием XSL - PullRequest
2 голосов
/ 20 сентября 2011

Мне нужно обернуть каждое слово тегом (например, span) в документе HTML, например:

<html>
<head>
    <title>It doesnt matter</title>
</head>
<body>
         <div> Text in a div </div>
         <div>
    Text in a div
    <p>
        Text inside a p
    </p>
     </div>
</body>
</html>

Чтобы получить что-то вроде этого:

<html>
<head>
    <title>It doesnt matter</title>
</head>
<body>
         <div> <span>Text </span> <span> in </span> <span> a </span> <span> div </span> </div>
         <div>

             <span>Text </span> <span> in </span> <span> a </span> <span> div </span>                     
             <p>
               <span>Text </span> <span> in </span> <span> a </span> <span> p </span> 
             </p>
     </div>
</body>
</html>

Это важно длясохранить структуру тела ...

Любая помощь?

Ответы [ 2 ]

2 голосов
/ 20 сентября 2011

Все три нижеприведенных решения используют шаблон проектирования XSLT, который переопределяет правило идентификации для общего сохранения структуры и содержимого документа XML и изменения только определенных узлов.

I.Решение XSLT 1.0 :

Это короткое и простое преобразование (нигде не используется <xsl:choose>):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" 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="*[not(self::title)]/text()"
               name="split">
  <xsl:param name="pText" select=
       "concat(normalize-space(.), ' ')"/>

  <xsl:if test="string-length(normalize-space($pText)) >0">
   <span>
   <xsl:value-of select=
        "substring-before($pText, ' ')"/>
   </span>

   <xsl:call-template name="split">
    <xsl:with-param name="pText"
         select="substring-after($pText, ' ')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

применительно к предоставленномуXML-документ :

<html>
    <head>
        <title>It doesnt matter</title>
    </head>
    <body>
        <div> Text in a div </div>
        <div>
         Text in a div
            <p>
             Text inside a p
         </p>
        </div>
    </body>
</html>

дает требуемый, правильный результат :

<html>
   <head>
      <title>It doesnt matter</title>
   </head>
   <body>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
      </div>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
         <p>
            <span>Text</span>
            <span>inside</span>
            <span>a</span>
            <span>p</span>
         </p>
      </div>
   </body>
</html>

II.Решение XSLT 2.0:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" 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="*[not(self::title)]/text()">
  <xsl:for-each select="tokenize(., '[\s]')[.]">
   <span><xsl:sequence select="."/></span>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

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

<html>
   <head>
      <title>It doesnt matter</title>
   </head>
   <body>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
      </div>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
         <p>
            <span>Text</span>
            <span>inside</span>
            <span>a</span>
            <span>p</span>
         </p>
      </div>
   </body>
</html>

III Решение с использованием FXSL :

Используя шаблон / функцию str-split-to-words FXSL, можно легко реализовать гораздо более сложный токенизацию - в любой версииXSLT:

Давайте создадим более сложный XML-документ и правила токенизации :

<html>
    <head>
        <title>It doesnt matter</title>
    </head>
    <body>
        <div> Text: in a div </div>
        <div>
         Text; in; a. div
            <p>
             Text- inside [a] [p]
         </p>
        </div>
    </body>
</html>

Здесь имеется более одного разделителя, который указывает начало или конецслово.В этом конкретном примере разделителями могут быть: " ", ";", ".", ":", "-", "[", "]".

В следующем преобразовании используютсяFXSL для этого более сложного токенизации :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">

   <xsl:import href="strSplit-to-Words.xsl"/>

   <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
   <xsl:strip-space elements="*"/>

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

    <xsl:template match="*[not(self::title)]/text()">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-to-words">
          <xsl:with-param name="pStr" select="normalize-space(.)"/>
          <xsl:with-param name="pDelimiters" 
                          select="' ;.:-[]'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>

    <xsl:template match="word[string-length(normalize-space(.)) > 0]">
      <span>
        <xsl:value-of select="."/>
      </span>
    </xsl:template>
</xsl:stylesheet>

и выдает нужный, правильный результат :

<html>
   <head>
      <title>It doesnt matter</title>
   </head>
   <body>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
      </div>
      <div>
         <span>Text</span>
         <span>in</span>
         <span>a</span>
         <span>div</span>
         <p>
            <span>Text</span>
            <span>inside</span>
            <span>a</span>
            <span>p</span>
            <word/>
         </p>
      </div>
   </body>
</html>
1 голос
/ 20 сентября 2011

Этого можно достичь, расширив преобразование идентификаторов, включив в него рекурсивный шаблон, который проверяет наличие пробелов в фрагменте текста и, если это так, помещает тег span вокруг первого слова.Затем он может рекурсивно вызывать себя для оставшейся части текста.

Вот оно в действии ...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" indent="yes"/>

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

   <!-- Don't split the words in the title -->
   <xsl:template match="title">
      <xsl:copy-of select="." />
   </xsl:template>

   <!-- Matches a text element. Given a name so it can be recursively called -->
   <xsl:template match="text()" name="wrapper">
      <xsl:param name="text" select="." />
      <xsl:variable name="new" select="normalize-space($text)" />
      <xsl:choose>
         <xsl:when test="contains($new, ' ')">
            <span><xsl:value-of select="concat(substring-before($new, ' '), ' ')" /></span>
            <xsl:call-template name="wrapper">
               <xsl:with-param name="text" select="substring-after($new, ' ')" />
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <span><xsl:value-of select="$new" /></span>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

При вызове вашего образца HTML вывод будет следующим:

<html>
   <head>
      <title>It doesnt matter</title>
   </head>
   <body>
      <div>
         <span>Text </span>
         <span>in </span>
         <span>a </span>
         <span>div</span>
      </div>
      <div>
         <span>Text </span>
         <span>in </span>
         <span>a </span>
         <span>div</span>
         <p>
            <span>Text </span>
            <span>inside </span>
            <span>a </span>
            <span>p</span>
         </p>
      </div>
   </body>
</html>

Я не был на 100% уверен, насколько важны для вас пробелы в элементах span .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...