我正面临以下XSL转换案例。
我有这个:
<content>
xml_and_text_0
<break/>
xml_and_text_1
<break/>
...
<break/>
xml_and_text_n
</content>
我想使用XSL 2.0将上述XML转换为:
<content>
<block>
xml_and_text_0
</block>
<block>
xml_and_text_1
</block>
...
<block>
xml_and_text_n
</block>
</content>
(我也想忽略some_xml_and_text_k ='',但暂时让我们假设它们是非空的)
我想我可以使用类似于[XPath : select all following siblings until another sibling的方法,但也许有一种更简单的方法(或更简单的XPath表达式)。例如,是否可以匹配for-each循环中当前项目之后/之前的所有兄弟姐妹?
编辑:请注意,xml_and_text_i是文本和XML的混合,类似于XHTML,我想将其包含在内,如下所示:
<break/>
this is an <ref id = "123">example</ref>, which is really <citation>awesome</citation>
<break/>
会变成:
<block>this is an <ref id = "123">example</ref>, which is really <citation>awesome</citation></block>
答案 0 :(得分:2)
你的问题很混乱。如果您的实际输入包含break
节点之间的元素和文本节点,那么您的示例也应如此。
显然这个问题是关于分组并使用 XSLT 2.0 ,它可以很容易地解决:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/content">
<xsl:copy>
<xsl:for-each-group select="node()" group-starting-with="break">
<block>
<xsl:copy-of select="current-group()[not(self::break)]" />
</block>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
感谢michael.hor257k为您提供解决方案(对此感到抱歉)。您的方法看起来比我的更清洁,如下所示并基于XSL轴。我的版本也考虑边缘情况,但我猜你的版本可以适应同样的情况,我会调查它。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<!-- xsl:output method="xml" indent="yes" / -->
<xsl:template match="/content">
<content>
<xsl:choose>
<!-- First of all, distinguish the case where there is at least one separator, from the ones with no separator at all -->
<xsl:when test="break">
<xsl:for-each select="break">
<!-- What do we have before the first separator? Create a block only if non-empty stuff -->
<xsl:if test="position() = 1">
<xsl:variable name="first_block"><xsl:copy-of select="preceding-sibling::node()" /></xsl:variable>
<xsl:if test = "normalize-space ( $first_block ) != ''" >
<xsl:message select="concat ( '1|', $first_block, '|' )" />
<block id = "{@id}"><xsl:copy-of select="$first_block" /></block>
</xsl:if>
</xsl:if>
<!-- What do we have after the next separator and before the next (or the end)? -->
<xsl:variable name="block_content">
<xsl:choose>
<xsl:when test="following-sibling::break">
<!-- select all that comes after current node and precedes the next separator -->
<xsl:copy-of select="following-sibling::node() intersect following-sibling::break[1]/preceding-sibling::node()" />
</xsl:when>
<xsl:otherwise>
<!-- One separator after another, without anything in between -->
<xsl:copy-of select="following-sibling::node()" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:message select="concat ( '|', $block_content, '|' )" />
<xsl:message select="concat ( '_|', normalize-space ( $block_content ), '|_' )" />
<!-- Did we get something after the current separator? Create a block if yes -->
<xsl:if test = "normalize-space( $block_content ) != ''">
<block id = "{@id}"><xsl:copy-of select="$block_content" /></block>
</xsl:if>
</xsl:for-each>
</xsl:when>
<!-- When some content is available without any separator, create a virtual single block to represent it -->
<xsl:otherwise>
<xsl:variable name="single_block"><xsl:copy-of select="node()" /></xsl:variable>
<xsl:if test = "normalize-space( $single_block ) != ''">
<block id = "00"><xsl:copy-of select = "$single_block" /></block>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</content>
</xsl:template>
</xsl:stylesheet>
鉴于此输入:
<content>
Some <b>text</b>
<break id = '1'/>
this is an <ref id = "123">example</ref>, which is really <citation>awesome</citation>
<break id = '2'/>
Some other text
</content>
它产生了这个输出:
<?xml version="1.0" encoding="UTF-8"?>
<content>
<block id="1"> Some <b>text</b></block>
<block id="1"> this is an <ref id="123">example</ref>, which is really<citation>awesome</citation></block>
<block id="2"> Some other text </block>
</content>