代码之家  ›  专栏  ›  技术社区  ›  Neil Albrock

用XSLT截断XML

  •  7
  • Neil Albrock  · 技术社区  · 17 年前

    下面是由 Symphony CMS .

       <news>
            <entry>
                <title>Lorem Ipsum</title>
                <body>
                    <p><strong>Lorem Ipsum</strong></p>
                    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
                    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna.</p>
                </body>
            </entry>
        </news>
    

    <body> 元素,基于指定的长度,以博客样式显示:

    Lorem ipsum dolor sit amet, 献祭精英。塞德 malesuada auctor magna。乌尔纳维瓦姆斯酒店 马莱苏亚达,阿库姆桑,马萨。奎斯克 我是purus,孕妇eget,Ultrices a, 门在,扫描电镜。。。 更多

    哪里 是指向完整新闻项目的链接。我知道我可以选择特定的段落,我也知道我可以使用substring函数带来指定数量的字符。但是,我需要保留文本的格式,即 < 要素

    我意识到这会引起标签关闭的问题,但肯定有办法。希望对XSLT更有经验的人能够对此问题有所了解。

    5 回复  |  直到 12 年前
        1
  •  5
  •   Chaotic Pattern    17 年前

    这是我的版本。我已经在您的XML样本上测试了它,它是有效的。

    <xsl:apply-templates select="path/to/body/*" mode="truncate"/> .

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:strip-space elements="*"/>
    
    <!-- limit: the truncation limit -->
    <xsl:variable name="limit" select="250"/>
    
    <!-- t: Total number of characters in the set -->
    <xsl:variable name="t" select="string-length(normalize-space(//body))"/>
    
    <xsl:template match="*" mode="truncate">
        <xsl:variable name="preceding-strings">
            <xsl:copy-of select="preceding::text()[ancestor::body]"/>
        </xsl:variable>
    
        <!-- p: number of characters up to the current node -->
        <xsl:variable name="p" select="string-length(normalize-space($preceding-strings))"/>
    
        <xsl:if test="$p &lt; $limit">
            <xsl:element name="{name()}">
                <xsl:apply-templates select="@*" mode="truncate"/>
                <xsl:apply-templates mode="truncate"/>
            </xsl:element>
        </xsl:if>
    </xsl:template>
    
    <xsl:template match="text()" mode="truncate">
        <xsl:variable name="preceding-strings">
            <xsl:copy-of select="preceding::text()[ancestor::body]"/>
        </xsl:variable>
    
        <!-- p: number of characters up to the current node -->
        <xsl:variable name="p" select="string-length(normalize-space($preceding-strings))"/>
    
        <!-- c: number of characters including current node -->
        <xsl:variable name="c" select="$p + string-length(.)"/>
    
        <xsl:choose>
            <xsl:when test="$limit &lt;= $c">
                <xsl:value-of select="substring(., 1, ($limit - $p))"/>
                <xsl:text>&#8230;</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
    <xsl:template match="@*" mode="truncate">
        <xsl:attribute name="{name(.)}"><xsl:value-of select="."/></xsl:attribute>
    </xsl:template>
    
    </xsl:stylesheet>
    
        2
  •  5
  •   Dimitre Novatchev    17 年前

    此XSLT转换:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common"
     xmlns:f="http://fxsl.sf.net/"
     xmlns:myAdd="f:myAdd"
     xmlns:myParam="f:myParam"
     exclude-result-prefixes="ext f myAdd myParam"
    >
     <xsl:import href="scanl.xsl"/>
     <!--                                         -->
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
     <!--                                         -->
     <myAdd:myAdd/>
     <myParam:myParam>0</myParam:myParam>
     <!--                                         -->
     <xsl:param name="pTruncateLength" select="772"/>
     <!--                                         -->
       <xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
       <xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
     <!--                                         -->
       <xsl:variable name="vrtfScanResults">
               <xsl:call-template name="scanl">
                 <xsl:with-param name="pFun" select="$vFun"/>
                 <xsl:with-param name="pQ0" select="$vZero" />
                 <xsl:with-param name="pList" select="/*/*/body//text()"/>
               </xsl:call-template>
       </xsl:variable>
     <!--                                         -->
       <xsl:variable name="vScanResults"
            select="ext:node-set($vrtfScanResults)"/>
       <xsl:variable name="vindNode" select=
        "count($vScanResults/*[. > $pTruncateLength][1]
                                       /preceding-sibling::*)"/>
     <!--                                         -->
       <xsl:variable name="vrtfTruncInfo">
           <xsl:for-each select="/*/*/body//text()">
     <!--                                         -->
             <xsl:variable name="vPos" select="position()"/>
             <tNode id="{generate-id()}">
               <xsl:attribute name="preserve">
                 <xsl:if test="$vPos &lt; $vindNode">
                   <xsl:value-of select="string-length(.)"/>
                 </xsl:if>
                 <xsl:if test="$vPos > $vindNode">
                   <xsl:value-of select="0"/>
                 </xsl:if>
                 <xsl:if test="$vPos = $vindNode">
                   <xsl:value-of select=
                   "$vScanResults/*[$vindNode+1]
                   -
                    $pTruncateLength"/>
                 </xsl:if>
               </xsl:attribute>
             </tNode>
           </xsl:for-each>
       </xsl:variable>
     <!--                                         -->
       <xsl:variable name="vTruncInfo" select="ext:node-set($vrtfTruncInfo)"/>
     <!--                                         -->
     <xsl:template match="node()|@*">
       <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
       </xsl:copy>
     </xsl:template>
     <!--                                         -->
     <xsl:template match="text()[ancestor::body]">
       <xsl:variable name="vAllowedLength"
            select="$vTruncInfo/*[@id = generate-id(current())]/@preserve"
       />
     <!--                                         -->
       <xsl:value-of select="substring(.,1,$vAllowedLength)"/>
    
       <xsl:if test="string-length(.) > $vAllowedLength
                   and
                     $vAllowedLength > 0
                    ">
         <strong> ...more</strong>
       </xsl:if>
     </xsl:template>
     <!--                                         -->
     <xsl:template match="myAdd:*" mode="f:FXSL">
       <xsl:param name="pArg1"/>
       <xsl:param name="pArg2"/>
       <xsl:value-of select="$pArg1 + string-length($pArg2)"/>
     </xsl:template>
    </xsl:stylesheet>
    

    应用于原始源XML文档时 :

    <news>
        <entry>
            <title>Lorem Ipsum</title>
            <body>
                <p>
                    <strong>Lorem Ipsum</strong>
                </p>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna.</p>
                <p>This text should not be displayed</p>
            </body>
        </entry>
    </news>
    

    :

    <news>
       <entry>
          <title>Lorem Ipsum</title>
          <body>
             <p>
                <strong>Lorem Ipsum</strong>
             </p>
             <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada auctor magna. Vivamus urna justo, pulvinar nec, sagittis malesuada, accumsan in, massa. Quisque mi purus, gravida eget, ultricies a, porta in, sem. Maecenas justo elit, elementum vel, feugiat vulputate, pulvinar nec, velit. Fusce vel ante et diam bibendum euismod. Nunc vel nulla non lorem dignissim placerat. Nulla magna massa, auctor et, tempor nec, auctor sit amet, turpis. Quisque odio lacus, auctor at, posuere id, suscipit eget, dui. Phasellus aliquam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin varius. Phasellus cursus. Cras mattis adipiscing turpis. Sed.</p>
             <p>Lorem <strong> ...more</strong>
             </p>
             <p/>
          </body>
       </entry>
    </news>
    

    以下是:

    1. 这个 scanl 来自 FXSL library myAdd:* )实际处理作为参数传递给 斯堪尔 样板必须传递给它的另一个参数是处理中的“初始”值,如果传递的项列表为空,则将返回该值。

    2. 全局参数 $pTruncateLength 保存最大字符串长度,超过该长度的文本必须被截断

        3
  •  3
  •   VonC    17 年前

    您需要的是XSLT ellipsis 发电机

    可能是这个 xslt 1.0 template 可能会给你一些想法:

    主要内容如下:

    <xsl:template match="text()" mode="label">
        <xsl:param name="self-x"/>
        <xsl:param name="self-y"/>
        <xsl:variable name="text" select="normalize-space(.)"/>
        <!-- a quick and dirty way to avoid problems with line breaks -->
        <!-- replace the select attribute with this call
             if you want to use a fancier way to escape whitespace
             characters:
              <xsl:call-template name="escape-ws"
                <xsl:with-param name="text" select="." /
              </xsl:call-template
        -->
        <use xlink:href="#text-box" transform="translate({$self-x} 
     {$self-y})"/>
        <!-- text nodes are marked with a little box -->
        <text x="{$self-x + $writing-bump-over}"
              y="{$self-y - $writing-bump-up}"
              style="{$text-font-style}; stroke:none; fill:{$text-color}">
          <xsl:text>"</xsl:text>
          <xsl:value-of select="substring($text,1,$max-text-length)"/>
          <!-- truncate the text node to $max-text-length -->
          <xsl:if test="string-length($text) &gt; $max-text-length">
            <!-- add an ellipsis if necessary -->
            <xsl:text>...</xsl:text>
          </xsl:if>
          <xsl:text>"</xsl:text>
        </text>
      </xsl:template>
    

    注:

    • 你需要用一个链接来替换省略号,但是主要的想法是存在的。
    • all script
    • 您可能不需要其中的所有内容:如果您需要“ <use xlink:href="... “,你需要 declare the xlink namespace
        4
  •  0
  •   Neil Albrock    17 年前

    经过多次黑客攻击,我找到了这个解决方案:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <!--
        Author: Neil Albrock
        Version: 1.0
        Description: Truncate by a character limit and retain HTML content.
        Usage: 
            <xsl:call-template name="truncate">
                <xsl:with-param name="data" select="path/to/your/body" />
                <xsl:with-param name="length" select="250" />
                <xsl:with-param name="link" select="'href'" />
            </xsl:call-template>
    -->
    
    <xsl:template name="truncate">
    
        <!-- The node set to be worked on. -->
        <xsl:param name="data"/>
        <!-- The desired truncate length. Default to length of data. -->
        <xsl:param name="length" select="string-length($data)"/>
        <!-- More link -->
        <xsl:param name="link"/>
    
        <xsl:choose>
            <!-- Return whole data if it's within length. -->
            <xsl:when test="string-length($data) &lt;= $length">
                <xsl:copy-of select="$data" />
            </xsl:when>
            <!-- Truncate to desired length. -->
            <xsl:otherwise>
                <xsl:for-each select="$data/*">
                    <xsl:variable name="this-node" select="string-length(.)"/>
                    <xsl:variable name="preceding-nodes">
                        <xsl:copy-of select="preceding-sibling::*"/>
                    </xsl:variable>
                    <xsl:variable name="node-sum" select="string-length(normalize-space($preceding-nodes))"/>
                    <xsl:variable name="limit" select="$node-sum + $this-node"/>
    
                    <xsl:choose>
                        <xsl:when test="$limit &gt; $length and $node-sum &lt;= $length">
                            <p>
                            <xsl:value-of select="substring(.,1,$length - $node-sum)"/>
                            <xsl:text>&#8230;</xsl:text>
                            <a>
                                <xsl:attribute name="href">
                                    <xsl:value-of select="$link"/>
                                </xsl:attribute>
                                <xsl:text>more</xsl:text>
                            </a>
                            </p>
                        </xsl:when>
                        <xsl:when test="$limit &lt; $length">
                            <xsl:copy-of select="."/>
                        </xsl:when>
                        <xsl:otherwise/>
                    </xsl:choose>
    
                </xsl:for-each>
            </xsl:otherwise>
        </xsl:choose>
    
    </xsl:template>
    
    </xsl:stylesheet>
    

    不过,我会使用混沌模式的解决方案,它更优雅;-)

        5
  •  -3
  •   Rob Di Marco    17 年前

    这将是使用XSLT的痛苦经历。我强烈建议使用Perl/Python之类的脚本语言来尝试这一点。

    推荐文章