我正在尝试在段落中换行新行而不删除混合节点中的HTML。我可以让一个或另一个工作,但不是两个。
XML:
<root>
<mixed html="true">
line 1
<a href="http://google.com">line 2</a>
<em>line 3</em>
</mixed>
</root>
期望的输出:
<div>
<p>line 1</p>
<p><a href="http://google.com">line 2</a></p>
<p><em>line 3</em></p>
</div>
这些模板与HTML匹配:
<xsl:template match="//*[@html]//*">
<xsl:element name="{name()}">
<xsl:apply-templates select="* | @* | text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="//*[@html]//@*">
<xsl:attribute name="{name(.)}">
<xsl:copy-of select="."/>
</xsl:attribute>
</xsl:template>
这些模板将新行转换为段落:
<xsl:template name="nl2p">
<xsl:param name="input" />
<xsl:variable name="output">
<xsl:call-template name="newline-to-paragraph">
<xsl:with-param name="input">
<xsl:copy-of select="$input" />
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$output" />
</xsl:template>
<!-- convert newline characters to <p></p> -->
<xsl:template name="newline-to-paragraph">
<xsl:param name="input" />
<xsl:variable name="output">
<xsl:choose>
<xsl:when test="contains($input, ' ')">
<xsl:if test="substring-before($input, ' ') != ''">
<xsl:element name="p"><xsl:copy-of select="substring-before($input, ' ')" /></xsl:element>
</xsl:if>
<xsl:call-template name="newline-to-paragraph">
<xsl:with-param name="input">
<xsl:copy-of select="substring-after($input, ' ')" />
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$input != ''">
<xsl:element name="p"><xsl:copy-of select="$input" /></xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:copy-of select="$output" />
</xsl:template>
这可能吗?我意识到nl2p模板在节点集上运行字符串函数 - 这会破坏HTML吗?我可以保留它或使用特定的操作顺序来实现这个结果吗?
提前致谢。
编辑:我正在使用XSLT 1.0
答案 0 :(得分:2)
编辑:抱歉,我错过了拆分文字节点!
最常见的问题:使用p
元素包装非空混合内容行
这里的问题是输入树提供程序如何处理仅空白区域的文本节点。
只有Saxon似乎只保留空白区域的文本节点...当然,在输入中添加xml:space="preserve"
,解决了每个其他XSLT处理器的问题。
此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:preserve-space elements="*" />
<xsl:template match="*[@html='true' or @nl2p='true']">
<div>
<xsl:apply-templates select="node()[1]"/>
</div>
</xsl:template>
<xsl:template match="node()" mode="open" name="open">
<xsl:copy-of select="." />
<xsl:apply-templates select="following-sibling::node()[1]"
mode="open" />
</xsl:template>
<xsl:template match="*[@html='true' or @nl2p='true']/node()">
<xsl:param name="pTail" select="''" />
<p>
<xsl:value-of select="$pTail" />
<xsl:call-template name="open" />
</p>
<xsl:variable name="vNext"
select="following-sibling::text()[contains(., '
')][1]" />
<xsl:apply-templates select="$vNext">
<xsl:with-param name="pString"
select="substring-after($vNext, '
')" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="text()[contains(., '
')]"
mode="open" priority="1">
<xsl:value-of select="substring-before(., '
')" />
</xsl:template>
<xsl:template match="*[@html='true' or @nl2p='true']
/text()[contains(., '
')]"
priority="1" name="text">
<xsl:param name="pString" select="."/>
<xsl:choose>
<xsl:when test="contains($pString, '
')">
<xsl:variable name="vOutput"
select="normalize-space(substring-before($pString, '
'))" />
<xsl:if test="$vOutput">
<p>
<xsl:value-of select="$vOutput"/>
</p>
</xsl:if>
<xsl:call-template name="text">
<xsl:with-param name="pString"
select="substring-after($pString, '
')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pTail" select="$pString" />
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
使用此输入(比问题更复杂):
<root>
<mixed html="true" xml:space="preserve">
line 1
line 2
<a href="http://google.com">line 2</a> after
before <em>line 3</em><img src="http://example.org"/>
</mixed>
</root>
输出:
<div>
<p>line 1</p>
<p>line 2</p>
<p> <a href="http://google.com">line 2</a> after</p>
<p> before <em>line 3</em><img src="http://example.org" /></p>
</div>
减少问题:用非p
元素包装非空文本节点行和每个其他节点子元素
此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@html='true']/*">
<p>
<xsl:call-template name="identity"/>
</p>
</xsl:template>
<xsl:template match="*[@html='true']/text()" name="text">
<xsl:param name="pString" select="."/>
<xsl:choose>
<xsl:when test="contains($pString,'
')">
<xsl:call-template name="text">
<xsl:with-param name="pString"
select="substring-before($pString,'
')"/>
</xsl:call-template>
<xsl:call-template name="text">
<xsl:with-param name="pString"
select="substring-after($pString,'
')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="normalize-space($pString)">
<p>
<xsl:value-of select="normalize-space($pString)"/>
</p>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
使用问题输入样本,输出:
<root>
<mixed html="true">
<p>line 1</p>
<p><a href="http://google.com">line 2</a></p>
<p><em>line 3</em></p>
</mixed>
</root>
使用我自己更复杂的输入(没有@xml:space
):
<root>
<mixed html="true">
line 1
line 2
<a href="http://google.com">line 2</a> after
before <em>line 3</em><img src="http://example.org"/>
</mixed>
</root>
输出:
<root>
<mixed html="true">
<p>line 1</p>
<p>line 2</p>
<p><a href="http://google.com">line 2</a></p>
<p>after</p>
<p>before</p>
<p><em>line 3</em></p>
<p><img src="http://example.org"></img></p>
</mixed>
</root>
答案 1 :(得分:1)
在我看到你的评论之前我已经开发了这个,你已经坚持使用1.0了。但是你说你对2.0感到好奇,所以这就是:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes"/>
<!-- Identity transform -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- surround all other elements with <p> -->
<xsl:template match="*" priority="1">
<p><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy></p>
</xsl:template>
<!-- recurse through root and mixed elements, but don't copy them. -->
<xsl:template match="root | mixed" priority="2">
<xsl:apply-templates select="node()"/>
</xsl:template>
<!-- Surround non-space text content with <p> if there are
newlines in the text, or element siblings. -->
<xsl:template match="text()[contains(., '\n') or ../*]">
<xsl:analyze-string select="." regex="\s*\n\s*">
<xsl:non-matching-substring>
<p><xsl:value-of select="."/></p>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
鉴于输入:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<mixed html="true">
line 1
<a href="http://google.com">line 2</a>
<em>line 3</em>
</mixed>
</root>
它产生所需的输出:
<p>line 1</p>
<p><a href="http://google.com">line 2</a></p>
<p><em>line 3</em></p>
唯一需要XSLT 2.0的是<xsl:analyze-string>
。
您可以通过编写递归处理字符串的模板,使用规范化空间查找“\ n”字符,并使用<p>
包围剩余的文本片段来执行类似的操作。
答案 2 :(得分:1)
略微纠正您的转型:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@html='true']">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="*[@html='true']/*">
<p><xsl:call-template name="identity"/></p>
</xsl:template>
<xsl:template match="*[@html='true']/text()">
<xsl:call-template name="nl2p"/>
</xsl:template>
<xsl:template name="nl2p">
<xsl:param name="input" select="."/>
<xsl:variable name="output">
<xsl:call-template name="newline-to-paragraph">
<xsl:with-param name="input">
<xsl:copy-of select="$input" />
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$output" />
</xsl:template>
<!-- convert newline characters to <p></p> -->
<xsl:template name="newline-to-paragraph">
<xsl:param name="input" />
<xsl:variable name="output">
<xsl:variable name="vlineText"
select="normalize-space(substring-before($input, ' '))"/>
<xsl:variable name="vtextAfter"
select="normalize-space(substring-after($input, ' '))"/>
<xsl:choose>
<xsl:when test="contains($input, ' ')">
<xsl:if test="$vlineText">
<p><xsl:copy-of select="$vlineText"/></p>
</xsl:if>
<xsl:call-template name="newline-to-paragraph">
<xsl:with-param name="input" select="$vtextAfter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="normalize-space($input)">
<p><xsl:copy-of select="$input" /></p>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:copy-of select="$output" />
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<root>
<mixed html="true">
line 1
<a href="http://google.com">line 2</a>
<em>line 3</em>
</mixed>
</root>
产生想要的结果:
<div>
<p>line 1</p>
<p>
<a href="http://google.com">line 2</a>
</p>
<p>
<em>line 3</em>
</p>
</div>