使用XSLT 1.0在每两个处理指令之间对XML节点进行分组

时间:2016-03-17 08:17:27

标签: xslt xpath

使用XSLT 1.0,我需要在处理指令<?start?><?end?>之间对每个节点进行分组。此对外的节点应在输出处保持不变。

首先,我需要找到一种方法,如何只选择每个起始端对之间的节点。假设我们有一个示例输入XML:

<root>
  abc
  <?start?>
    def<Highlighted bold="yes">
    <Highlighted italic="yes">ghi</Highlighted>
    </Highlighted>jkl
    <?pi?>
    <table>
      <Caption>stu</Caption>
    </table>vw
  <?end?>
  xy
  <?start?> 
  abc <Caption>def</Caption> ghi
  <?end?>
  jkl
</root>

此外,我还需要在输出端的“开始 - 结束”交叉点之外设置节点。这意味着在输出处:a)起始端PI的交叉点处的节点将位于组元素b)交叉点外的任何节点将保持不变。请注意,输入文档也可能没有开始 - 结束处理指令对。

例如,根据给定的输入,输出应如下所示:

<root>
  abc
  <group>
    def<Highlighted bold="yes">
    <Highlighted italic="yes">ghi</Highlighted>
    </Highlighted>jkl
    <?pi?>
    <table>
      <Caption>stu</Caption>
    </table>vw
   </group>
   xy
   <group> 
     abc <Caption>def</Caption> ghi
   </group>
   jkl
 <root>

此问题的一部分已在Finding all XML nodes between each two processing instructions中得到解答。但是我很难打印出group元素之外的节点而不重复任何节点。

3 个答案:

答案 0 :(得分:2)

代码:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>

    <xsl:template match="processing-instruction('start')">
        <group>
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </group>
        <xsl:apply-templates select="following-sibling::processing-instruction('end')[1]/following-sibling::node()[1]"/>
    </xsl:template>

    <xsl:template match="processing-instruction('end')"/>

</xsl:stylesheet>

此XSLT使用递归方法处理第一个子节点和第一个后续兄弟节点()。

第一个模板处理除开始和结束pis之外的所有节点。 第二个模板将group元素添加到start pi和end pi之间的元素。第三个模板有助于删除结束pi。

答案 1 :(得分:0)

root的模板更改为

<xsl:template match="root">
  <xsl:copy>
    <xsl:apply-templates select="key('start', '') | processing-instruction('start') | node()[preceding-sibling::processing-instruction()[1][self::processing-instruction('end')]] | key('end', '')"/>          
  </xsl:copy>
</xsl:template>

所以整个代码变成

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output indent="yes"/>

    <xsl:key name="start" match="root/node()[not(self::processing-instruction('start'))]" use="generate-id(preceding-sibling::processing-instruction('start')[1])"/>
    <xsl:key name="end" match="root/node()[not(self::processing-instruction('end'))]" use="generate-id(following-sibling::processing-instruction('end')[1])"/>

    <xsl:template match="root">
      <xsl:copy>
        <xsl:apply-templates select="key('start', '') | processing-instruction('start') | node()[preceding-sibling::processing-instruction()[1][self::processing-instruction('end')]] | key('end', '')"/>          
      </xsl:copy>
    </xsl:template>

    <xsl:template match="processing-instruction('start')">
        <xsl:variable name="end" select="following-sibling::processing-instruction('end')[1]"/>
        <xsl:variable name="following-start" select="key('start', generate-id())"/>
        <xsl:variable name="preceding-end" select="key('end', generate-id($end))"/>
        <xsl:variable name="intersect" select="$following-start[count(. | $preceding-end) = count($preceding-end)]"/>
        <group>
            <xsl:copy-of select="$intersect"/>
        </group>
    </xsl:template>

</xsl:stylesheet>

测试不是很好,但我希望它涵盖了pis之前,之间和之后的所有节点。

答案 2 :(得分:0)

即使我喜欢@Lingamurthy CS的递归解决方案 她的解决方案略有不同。

isValidJson(jsonstr)