XSLT - использование подстроки с копией для сохранения внутренних тегов HTML - PullRequest
3 голосов
/ 02 августа 2011

У меня есть такой 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> всякий раз, когда беру подстроку. Можно ли получить первые 500 символов тега story, сохранив внутренние теги HTML?

Спасибо!

Ответы [ 2 ]

3 голосов
/ 04 августа 2011

Вот еще один подход в 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> (и не вложены на произвольную глубину).Это делает всю проблему намного проще.

Вот один из способов сделать это: чтобы получить содержимое первой части, вы можете использовать шаблон, который будет обрабатывать набор узлов одного уровня, пока максимальная длина строки не будетпревышен, и br или 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> с.

  <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>
0 голосов
/ 03 августа 2011

Очень похожий вопрос на Получить вводный текст из N символов с XSLT 1.0 из 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>

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

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

Когда 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>
...