我对SO社区的聪明人提出了一个问题。
以下是Symphony CMS生成的一小段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>
</body>
</entry>
</news>
我需要做的是根据指定的长度获取<body>
元素的一部分,以便在以下博客样式中显示:
Lorem ipsum dolor sit amet, 奉献精神。桑达 malesuada auctor magna。 Vivamus urna justo,pulvinar nec,sagittis malesuada,accumsan in,massa。 Quisque mi purus,gravida eget,ultricies a, porta in,sem ... more
...其中更多是指向完整新闻项的链接。我知道我可以选择特定的段落,我也知道我可以使用substring函数来带来指定数量的字符。但是,我需要保留文本的格式,即<body>
元素中的HTML标记。
我意识到这会引发标签关闭的问题,但肯定有办法。希望有更多XSLT经验的人能够解释这个问题。
答案 0 :(得分:5)
这是一个完整的XSLT 1.0转换,可以解决问题。
此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 < $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>
请注意以下内容:
导入FXSL library中的 scanl
样式表。此模板通常用于累积处理项目列表的数据。执行实际处理的函数(匹配myAdd:*
的模板)作为参数传递给scanl
模板。必须传递给它的另一个参数是处理中的“初始”值,如果传递的项目列表为空,则返回该值。
全局参数 $pTruncateLength
包含超出文本必须截断的最大字符串长度
答案 1 :(得分:5)
这是我的版本。我已经对你的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 < $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 <= $c">
<xsl:value-of select="substring(., 1, ($limit - $p))"/>
<xsl:text>…</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 :(得分:3)
您要问的是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) > $max-text-length">
<!-- add an ellipsis if necessary -->
<xsl:text>...</xsl:text>
</xsl:if>
<xsl:text>"</xsl:text>
</text>
</xsl:template>
注意:
<use xlink:href="...
”,则需要declare the xlink namespace 答案 3 :(得分:0)
经过多次黑客攻击后,我找到了解决方案:
<?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) <= $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 > $length and $node-sum <= $length">
<p>
<xsl:value-of select="substring(.,1,$length - $node-sum)"/>
<xsl:text>…</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 < $length">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
我会使用Chaotic Pattern的解决方案,但它更优雅; - )
答案 4 :(得分:-3)
这将是一个使用XSLT的痛苦事件。我强烈建议使用像Perl / Python这样的脚本语言来尝试这个。