3

我有一些这样的 XML:

<story><p><strong>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</strong>Nulla vel mauris metus. Etiam vel tortor vel magna bibendum euismod nec varius turpis. Nullam ullamcorper, nunc vel auctor consectetur, quam felis accumsan eros, lacinia fringilla mauris est vel lectus. Curabitur et tortor eros. Duis sed convallis metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempus quam sed enim gravida bibendum. Vestibulum magna ligula, varius in sodales eu, ultricies volutpat sem. Phasellus ante justo, vestibulum eu hendrerit a, posuere vitae est. Integer at pulvinar est.</p><p>Quisque a commodo eros. Integer tempus mi sit amet leo consectetur adipiscing. Nullam sit amet enim metus. Curabitur sollicitudin egestas arcu, at convallis enim iaculis eget. Etiam faucibus, justo sit amet lacinia consectetur, purus nunc rhoncus dui, id malesuada tortor est sed orci. Quisque eget nisi vitae mi facilisis varius. Integer fringilla eros sit amet velit vehicula commodo. </p><br /><span>And some more text here</span> </story>

我想做这个:

<xsl:copy-of select="substring(story/node(),1,500)"/>

这是问题所在。每当我获取子字符串时,我都会丢失<p>, <strong>, <br />标签内的 HTML 和其他标签。<story>有没有办法在保留内部 HTML 标签的同时获取故事标签的前 500 个字符?

谢谢!

4

2 回答 2

3

这是 XSLT 1.0 中的另一种方法,无需使用node-set扩展:

  <xsl:template match="@*|node()" mode="limit-length">
    <xsl:param name="length"/>
    <xsl:copy>
      <xsl:apply-templates select="@*" mode="limit-length"/>
      <xsl:call-template name="copy-nodes">
        <xsl:with-param name="nodes" select="node()"/>
        <xsl:with-param name="length" select="$length"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="text()" mode="limit-length">
    <xsl:param name="length"/>
    <xsl:value-of select="substring(., 1, $length)"/>
  </xsl:template>

  <xsl:template name="copy-nodes">
    <xsl:param name="nodes"/>
    <xsl:param name="length"/>
    <xsl:if test="$length &gt; 0 and $nodes">
      <xsl:variable name="head" select="$nodes[1]"/>
      <xsl:apply-templates select="$head" mode="limit-length">
        <xsl:with-param name="length" select="$length"/>
      </xsl:apply-templates>
      <xsl:variable name="remaining" select="$length - string-length($head)"/>
      <xsl:if test="$remaining &gt; 0 and count($nodes) &gt; 1">
        <xsl:call-template name="copy-nodes">
          <xsl:with-param name="nodes" select="$nodes[position() &gt; 1]"/>
          <xsl:with-param name="length" select="$remaining"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:if>
  </xsl:template>

基本上这是身份模板,将子节点复制到一个递归模板,该模板负责保持最大字符串长度,加上一个单独的文本节点模板,将它们截断为最大长度。

您可以为示例输入调用它,如下所示:

<xsl:call-template name="copy-nodes">
  <xsl:with-param name="nodes" select="story/node()"/>
  <xsl:with-param name="length" select="500"/>
</xsl:call-template>

后续:拆分故事

对于在第一个中断或 N 个字符后的段落结束后将故事分成两部分的后续问题,我将继续做一个简化假设,即您只想<p>考虑<br><story>元素(并且不嵌套在任意深度)。这使整个问题变得容易得多。

这是实现它的一种方法:要获取第一部分的内容,您可以使用一个模板,该模板将处理一组兄弟节点,直到超过最大字符串长度并遇到 a bror p,然后停止。

  <xsl:template match="node()" mode="before-break">
    <xsl:param name="length"/>
    <xsl:if test="$length &gt; 0 or not(self::br or self::p)">
      <xsl:copy-of select="."/>
      <xsl:apply-templates select="following-sibling::node()[1]"
                           mode="before-break">
        <xsl:with-param name="length" select="$length - string-length(.)"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:template>

对于第二部分,您可以创建另一个模板,该模板搜索与前一个模板相同的条件,但在此之后不输出任何内容:

  <xsl:template match="node()" mode="after-break">
    <xsl:param name="length"/>
    <xsl:choose>
      <xsl:when test="$length &gt; 0 or not(self::br or self::p)">
        <xsl:apply-templates select="following-sibling::node()[1]"
                             mode="after-break">
          <xsl:with-param name="length" select="$length - string-length(.)"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="not(self::br)"> <!-- suppress the <br/> -->
          <xsl:copy-of select="."/>
        </xsl:if>
        <xsl:copy-of select="following-sibling::node()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

以下是如何使用这些模板将故事分成两个<div>s。

  <xsl:template match="story">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <div>
        <xsl:apply-templates select="node()[1]" mode="before-break">
          <xsl:with-param name="length" select="500"/>
        </xsl:apply-templates>
      </div>
      <div>
        <xsl:apply-templates select="node()[1]" mode="after-break">
          <xsl:with-param name="length" select="500"/>
        </xsl:apply-templates>
      </div>
    </xsl:copy>
  </xsl:template>
于 2011-08-03T22:10:17.013 回答
0

Get N characters introduction text with XSLT 1.0 from XHTML有非常相似的问题

这是 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
   <xsl:param name="MAXCHARS">500</xsl:param>

   <xsl:template match="/body">
      <xsl:apply-templates select="child::node()"/>
   </xsl:template>

   <xsl:template match="node()">
      <xsl:param name="LengthToParent">0</xsl:param>

      <!-- Get length of previous siblings -->
      <xsl:variable name="previousSizes">
         <xsl:for-each select="preceding-sibling::node()">
            <length>
               <xsl:value-of select="string-length(.)"/>
            </length>
         </xsl:for-each>
      </xsl:variable>
      <xsl:variable name="LengthToNode" select="sum(msxsl:node-set($previousSizes)/length)"/>

      <!-- Total amount of characters processed so far -->
      <xsl:variable name="LengthSoFar" select="$LengthToNode + number($LengthToParent)"/>

      <!-- Check limit is not exceeded -->
      <xsl:if test="$LengthSoFar &lt; number($MAXCHARS)">
         <xsl:choose>
            <xsl:when test="self::text()">
               <!-- Output text nonde with ... if required -->
               <xsl:value-of select="substring(., 1, number($MAXCHARS) - $LengthSoFar)"/>
               <xsl:if test="string-length(.) &gt; number($MAXCHARS) - $LengthSoFar">...</xsl:if>
            </xsl:when>
            <xsl:otherwise>
               <!-- Output copy of node and recursively call template on its children -->
               <xsl:copy>
                  <xsl:copy-of select="@*"/>
                  <xsl:apply-templates select="child::node()">
                     <xsl:with-param name="LengthToParent" select="$LengthSoFar"/>
                  </xsl:apply-templates>
               </xsl:copy>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:if>
   </xsl:template>

</xsl:stylesheet>

它的工作原理是遍历节点的子节点,并将到该点的前面兄弟节点的长度相加。请注意,获取前面兄弟的长度的代码需要使用 node-set 函数,它是 XSLT 1.0 的扩展函数。在我的示例中,我使用的是 Microsoft 扩展功能。

如果节点不是文本节点,则到该点的字符总长度将是前面兄弟节点的长度之和,将父节点前面兄弟节点的总和(作为参数传递给模板)。

当 XSLT 应用于您的输入 XML 时,将输出以下内容:

<story>
    <p>
        <strong>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</strong>Nulla vel mauris metus. Etiam vel tortor vel magna bibendum euismod nec varius turpis. Nullam ullamcorper, nunc vel auctor consectetur, quam felis accumsan eros, lacinia fringilla mauris est vel lectus. Curabitur et tortor eros. Duis sed convallis metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempus quam sed enim gravida bibendum. Vestibulum magna ligula, varius in sodales eu, ultr...
    </p>
</story>
于 2011-08-03T16:54:08.047 回答