html - 使用 XSLT 选择包含 HTML 标签的 n 个词的摘要

标签 html xslt parsing

我想选择一个摘要,以及使用 XSLT 的 HTML 格式元素。这是一个 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> 等)。

最佳答案

您可能最好以编程方式执行此操作,而不是使用纯 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-and-sorting-with-xslt-1-0

(请注意,在 XSLT2.0 中,我相信有一个 tokenize 函数,可以简化上面的操作)

这会给你这样的 XML...

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

等等……

接下来是遍历这个XML文档的情况,使用另一个XSLT文档,只输出最多前45个单词元素。为此,我反复应用一个模板,记录当前找到的 WORDS 总数。匹配节点时,存在三种可能

  • 匹配一个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 元素,只保留文本。这应该是相当简单的....

虽然这一切都非常糟糕,但这是我目前能想到的最好的!

关于html - 使用 XSLT 选择包含 HTML 标签的 n 个词的摘要,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2145294/

相关文章:

javascript - 在 Javascript 中将 RTF 转换为 HTML

html - 在水平滚动元素的子元素的全宽上显示背景色

jquery - 如何在使用 jquery 单击另一个图像时隐藏第一张图像?

xslt - 如何仅在xslt中选择第一个节点

xslt - XSLT 中的 @ (at) 符号有何用途?

parsing - 为带小数的正 JSON 数编写解析器

javascript - 如何获取 JSON 对象的模式并输出到包含类型的字符串

java - 以编程方式访问网页

HTML 标签默认样式问题?

xml - 不使用默认命名空间的没有前缀的 XSL 输出 XML?