XSL,匹配标记

时间:2016-08-24 18:18:11

标签: xml xslt xpath

我正面临以下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>

2 个答案:

答案 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>