Выбор реферата из n слов, включая теги HTML, с помощью XSLT - PullRequest
3 голосов
/ 27 января 2010

Я хочу выбрать реферат вместе с элементами формата HTML, используя XSLT. Вот пример XML:

<PUBLDES>The <IT>European Journal of Cancer (including EJC Supplements),</IT> 
is an international comprehensive oncology journal that publishes original 
research, editorial comments, review articles and news on experimental oncology, 
clinical oncology (medical, paediatric, radiation, surgical), translational 
oncology, and on cancer epidemiology and prevention. The Journal now has online
submission for authors. Please submit manuscripts at 
<SURL>http://ees.elsevier.com/ejc</SURL> and follow the instructions on the 
site.<P/>

The <IT>European Journal of Cancer (including EJC Supplements)</IT> is the 
official Journal of the European Organisation for Research and Treatment 
of Cancer (EORTC), the European CanCer Organisation (ECCO), the European 
Association for Cancer Research (EACR), the the European Society of Breast 
Cancer Specialists (EUSOMA) and the European School of Oncology (ESO). <P/>
Supplements to the <IT>European Journal of Cancer</IT> are published under 
the title <IT>EJC Supplements</IT> (ISSN 1359-6349).  All subscribers to 
<IT>European Journal of Cancer</IT> automatically receive this publication.<P/>
To access the latest tables of contents, abstracts and full-text articles 
from <IT>EJC</IT>, including Articles-in-Press, please visit <URL>
<HREF>http://www.sciencedirect.com/science/journal/09598049</HREF>
<HTXT>ScienceDirect</HTXT>
</URL>.</PUBLDES>

Как мне получить 45 слов из него вместе с тегами HTML в нем. Когда я использую substring() или concat(), он удаляет теги (например, <IT> и т. Д.).

1 Ответ

4 голосов
/ 28 января 2010

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

Первая таблица стилей будет копировать исходный XML, но «маркировать» любой текст, который он найдет, так что каждое слово в тексте станет отдельным элементом «WORD».

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <!-- Copy existing nodes and attributes -->
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
   <!-- Match text nodes -->
   <xsl:template match="text()">
      <xsl:call-template name="tokenize">
         <xsl:with-param name="string" select="."/>
      </xsl:call-template>
   </xsl:template>
   <!-- Splits a string into separate elements for each word -->
   <xsl:template name="tokenize">
      <xsl:param name="string"/>
      <xsl:param name="delimiter" select="' '"/>
      <xsl:choose>
         <xsl:when test="$delimiter and contains($string, $delimiter)">
            <xsl:variable name="word" select="normalize-space(substring-before($string, $delimiter))"/>
            <xsl:if test="string-length($word) &gt; 0">
               <WORD>
                  <xsl:value-of select="$word"/>
               </WORD>
            </xsl:if>
            <xsl:call-template name="tokenize">
               <xsl:with-param name="string" select="substring-after($string, $delimiter)"/>
               <xsl:with-param name="delimiter" select="$delimiter"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:variable name="word" select="normalize-space($string)"/>
            <xsl:if test="string-length($word) &gt; 0">
               <WORD>
                  <xsl:value-of select="$word"/>
               </WORD>
            </xsl:if>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Шаблон XSLT, используемый для «токенизации» строки текста, я взял из этого вопроса здесь:

tokenizing-сортировочный-с-1-0 XSLT

(обратите внимание, что в XSLT2.0, я полагаю, есть функция токенизации, которая упростит вышеперечисленное)

Это даст вам XML как ...

<PUBLDES>
   <WORD>The</WORD>
   <IT>
      <WORD>European</WORD>
      <WORD>Journal</WORD>
      <WORD>of</WORD>
      ....

И так далее ...

Далее, это случай обхода этого XML-документа с использованием другого XSLT-документа с выводом только до первых 45 словарных элементов. Чтобы сделать это, я неоднократно применяю шаблон, сохраняя промежуточную сумму количества найденных слов в настоящее время. При сопоставлении узла есть три возможности

  • Соответствует элементу WORD : Вывести его. Продолжайте обработку от следующего родного брата, если общее количество не достигнуто.
  • Соответствует элементу, где количество слов ниже него меньше, чем общее число : Скопируйте весь элемент, а затем продолжите обработку следующего брата, если общее количество не достигнуто
  • Соответствие элементам, в которых количество слов ниже будет превышать общее : скопировать текущий узел (но не его дочерние элементы) и продолжить обработку с первого дочернего элемента.

Вот таблица стилей, во всей ее отвратительности

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:variable name="WORDCOUNT">6</xsl:variable>

   <!-- Match root element -->
   <xsl:template match="/">
      <xsl:apply-templates select="descendant::*[1]" mode="word">
         <xsl:with-param name="previousWords">0</xsl:with-param>
      </xsl:apply-templates>
   </xsl:template>

   <!-- Match any node -->
   <xsl:template match="node()" mode="word">
      <xsl:param name="previousWords"/>

      <!-- Number of words below the element (at any depth) -->
      <xsl:variable name="childWords" select="count(descendant::WORD)"/>
      <xsl:choose>
         <!-- Matching a WORD element -->
         <xsl:when test="local-name(.) = 'WORD'">
            <!-- Copy the word -->
            <WORD>
               <xsl:value-of select="."/>
            </WORD>
            <!-- If there are still words to output, continue processing at next sibling -->
            <xsl:if test="$previousWords + 1 &lt; $WORDCOUNT">
               <xsl:apply-templates select="following-sibling::*[1]" mode="word">
                  <xsl:with-param name="previousWords">
                     <xsl:value-of select="$previousWords + 1"/>
                  </xsl:with-param>
               </xsl:apply-templates>
            </xsl:if>
         </xsl:when>

         <!-- Match a node where the number of words below it is within allowed limit -->
         <xsl:when test="$childWords &lt;= $WORDCOUNT - $previousWords">
            <!-- Copy the element -->
            <xsl:copy>
               <!-- Copy all its desecendants -->
               <xsl:copy-of select="*|@*"/>
            </xsl:copy>
            <!-- If there are still words to output, continue processing at next sibling -->
            <xsl:if test="$previousWords + $childWords &lt; $WORDCOUNT">
               <xsl:apply-templates select="following-sibling::*[1]" mode="word">
                  <xsl:with-param name="previousWords">
                     <xsl:value-of select="$previousWords + $childWords"/>
                  </xsl:with-param>
            </xsl:apply-templates>
         </xsl:if>
         </xsl:when>

         <!-- Match nodes where the number of words below it would exceed current limit -->
         <xsl:otherwise>
            <!-- Copy the node -->
            <xsl:copy>
               <!-- Continue processing at very first child node -->
               <xsl:apply-templates select="descendant::*[1]" mode="word">
                  <xsl:with-param name="previousWords">
                     <xsl:value-of select="$previousWords"/>
                  </xsl:with-param>
               </xsl:apply-templates>
            </xsl:copy>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Если вы выводите только первые 4 слова, скажем, это даст вам следующий вывод

<PUBLDES>
   <WORD>The</WORD>
   <IT>
      <WORD>European</WORD>
      <WORD>Journal</WORD>
      <WORD>of</WORD>
   </IT>
</PUBLDES>

Конечно, тогда вам понадобится еще одно преобразование, чтобы удалить элементы WORD и просто оставить текст. Это должно быть довольно просто ....

Это все очень неприятно, но это лучшее, что я мог придумать на данный момент!

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