在xml层次结构中向上移动分隔符元素 [英] Move separator elements upwards in xml hierarchy

查看:59
本文介绍了在xml层次结构中向上移动分隔符元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个XML文档,在层次结构的深处有分隔符.

I have an xml document with separators deep down in the hierarchy.

<A>
  <B>
    <C id='1'/>
    <separator/>
    <C id='2'/>
  </B>
  <B>
    <C id='3'/>
    <separator/>
  </B>
  <B>
    <C id='4'/>
  </B>
</A>

我想向上移动分隔符,使元素保持秩序.所以所需的输出是

I want to move the separators upwards, keeping elements in order. So the desired output is

<A>
  <B>
    <C id='1'/>
  </B>
</A>
<separator/>
<A>
  <B>
    <C id='2'/>
  </B>
  <B>
    <C id='3'/>
  </B>
</A>
<separator/>
<A>
  <B>
    <C id='4'/>
  </B>
</A>

仅使用xslt 1.0怎么做?仅使用模板匹配就可以在没有for-each的情况下完成此操作吗?

How can it be done using xslt 1.0 only? Can it be done without for-each, using template match only?

更新: 实际上,我得到了四个具有不同普遍性的出色答案,谢谢大家.

UPDATE: I actually got 4 brilliant answers of different levels of generality, thank you, guys.

推荐答案

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kCByFollSep" match="C"
             use="generate-id(following::separator[1])"/>
    <xsl:template match="A">
        <xsl:for-each select="B/separator|B[last()]/*[last()]">
            <A>
                <xsl:apply-templates
                     select="key('kCByFollSep',
                                 substring(generate-id(),
                                           1 div boolean(self::separator)))"/>
            </A>
            <xsl:copy-of select="self::separator"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="C">
        <B>
            <xsl:copy-of select="."/>
        </B>
    </xsl:template>
</xsl:stylesheet>

输出:

<A>
    <B>
        <C id="1" />
    </B>
</A>
<separator />
<A>
    <B>
        <C id="2" />
    </B>
    <B>
        <C id="3" />
    </B>
</A>
<separator />
<A>
    <B>
        <C id="4" />
    </B>
</A>

注意:按照separator进行分组,为可能的C添加最后的第三级元素,而不关注separator.

Note: Grouping by following separator, adding last third level element for posible C without following separator.

编辑:此样式表具有更多的拉式样式,与模式无关的另一种样式:

Edit: More pull style, more schema agnostic, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kCByFollSep" match="C"
             use="generate-id(following::separator[1])"/>
    <xsl:template match="text()"/>
    <xsl:template match="separator|*[not(*)][not(following::*)]">
        <A>
            <xsl:apply-templates
                     select="key('kCByFollSep',
                                 substring(generate-id(),
                                           1 div boolean(self::separator)))"
                     mode="output"/>
        </A>
        <xsl:copy-of select="self::separator"/>
    </xsl:template>
    <xsl:template match="C" mode="output">
        <B>
            <xsl:copy-of select="."/>
        </B>
    </xsl:template>
</xsl:stylesheet>

编辑2 :更通用的解决方案(我不信任的一件事,ja!)

EDIT 2: More general solution (one thing I do not trust, ja!)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:param name="pRemains"/>
        <xsl:copy>
            <xsl:apply-templates select="node()[descendant-or-self::node()
                                                   [not(self::separator)]
                                                   [count(following::separator)
                                                    = $pRemains]
                                               ][1]|@*">
                <xsl:with-param name="pRemains" select="$pRemains"/>
            </xsl:apply-templates>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()
                                        [descendant-or-self::node()
                                           [not(self::separator)]
                                           [count(following::separator)
                                            = $pRemains]
                                        ][1]">
            <xsl:with-param name="pRemains" select="$pRemains"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="/*">
        <xsl:variable name="vCurrent" select="."/>
        <xsl:for-each select="descendant::separator|node()[last()]">
            <xsl:variable name="vRemains" select="last()-position()"/>
            <xsl:for-each select="$vCurrent">
                <xsl:copy>
                    <xsl:apply-templates
                         select="node()[descendant::node()
                                          [not(self::separator)]
                                          [count(following::separator)
                                           = $vRemains]
                                       ][1]">
                        <xsl:with-param name="pRemains" select="$vRemains"/>
                    </xsl:apply-templates>
                </xsl:copy>
            </xsl:for-each>
            <xsl:copy-of select="self::separator"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="separator"/>
</xsl:stylesheet>

注意:通常是细粒度遍历.楼层层次结构规则(在本例中为根元素)复制自身和分隔符(最后一组的虚拟节点,而没有后面的分隔符)通过剩余的分隔符来处理第一个子对象,并具有足够的后续分隔符来处理.修改后的细粒度遍历标识规则,将其自身复制并再次处理第一个子对象,并使用足够的后续分隔符来处理同级对象.最后,一个分隔符规则打破了这一过程.

Note: Mostly a fine grained traversal. A floor hierarchy rule (in this case root element) copying itself and separator (dummy node for last group without following separator) passing remainder separators to process first child with enough following separators to process. A modified fine grained traversal identity rule, copying itself and again processing first child and following sibling with enough following separators to process. At last, a separator rule breaking the process.

编辑3 :其他更通用的解决方案,现在具有递归标识规则

Edit 3: Other more general solution, now with recursive identity rule

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
          use="generate-id((descendant::separator|following::separator)[1])"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:param name="pGroup"/>
        <xsl:copy>
            <xsl:apply-templates
               select="node()[descendant-or-self::node()[count(.|$pGroup)
                                                         = count($pGroup)]]|@*">
                <xsl:with-param name="pGroup" select="$pGroup"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/*">
        <xsl:variable name="vCurrent" select="."/>
        <xsl:for-each select="descendant::separator|node()[last()]">
            <xsl:variable name="vGroup"
                 select="key('kNodeByFolSep',generate-id(self::separator))"/>
            <xsl:for-each select="$vCurrent">
                <xsl:call-template name="identity">
                    <xsl:with-param name="pGroup" select="$vGroup"/>
                </xsl:call-template>
            </xsl:for-each>
            <xsl:copy-of select="self::separator"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="separator"/>
</xsl:stylesheet>

编辑4 :现在与以前相同,但是使用了关键测试而不是节点集交集.

Edit 4: Now just the same as before but with key test instead of node set intersection.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
          use="concat(generate-id(),'+',
                      generate-id((descendant::separator|
                                   following::separator)[1]))"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:param name="pSeparator"/>
        <xsl:copy>
            <xsl:apply-templates
               select="@*|node()[descendant-or-self::node()
                                    [key('kNodeByFolSep',
                                         concat(generate-id(),
                                                '+',
                                                $pSeparator))]]">
                <xsl:with-param name="pSeparator" select="$pSeparator"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/*">
        <xsl:variable name="vCurrent" select="."/>
        <xsl:for-each select="descendant::separator|node()[last()]">
            <xsl:variable name="vSeparator"
                          select="generate-id(self::separator)"/>
            <xsl:for-each select="$vCurrent">
                <xsl:call-template name="identity">
                    <xsl:with-param name="pSeparator" select="$vSeparator"/>
                </xsl:call-template>
            </xsl:for-each>
            <xsl:copy-of select="self::separator"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="separator"/>
</xsl:stylesheet>

这篇关于在xml层次结构中向上移动分隔符元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆