如何在XSLT中修复此消除转换?

时间:2012-06-01 00:35:13

标签: xml xslt

我有这个输入:

<root>
    <sector>
        <nodeA id="a">
            <section id="i">
                <item1 id="1" method="delete"/>

                <item1 id="1" method="create"> 
                    <somechild>a</somechild>
                </item1>
                <item1 id="1" method="change"> 
                    <somechild>a</somechild>
                </item1>
            </section>
            <section id="i">
                <item1 id="1" method="change"> 
                    <somechild>a</somechild>
                </item1>
            </section>
            <section id="i">
                <item1 id="1" method="delete"/>
                <item1 id="1" method="create">
                    <somechild>a</somechild>
                </item1>
            </section>
        </nodeA>
    </sector> 
</root>

我的XSL是:

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

    <xsl:template match="*[not(.//*[@id!=''])][@method='delete']">
        <xsl:if test="not(following::*[not(.//*[@id!=''])][@id=current()/@id][../@id = current()/../@id][generate-id(../..) = generate-id(current()/../..)])"/>       
    </xsl:template>    

    <xsl:template match="*[not(.//*[@id!=''])][@method!='delete']">
        <xsl:if test="not(following::*[not(.//*[@id!=''])][@method='delete'][@id=current()/@id][../@id = current()/../@id][generate-id(../..) = generate-id(current()/../..)])"/>
    </xsl:template>    

    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

我的输出:

<root>
    <sector>
        <nodeA id="a">
            <section id="i">
            </section>
            <section id="i">
            </section>
            <section id="i">
            </section>
        </nodeA>
    </sector>
</root>

预期输出:

<root>
    <sector>
        <nodeA id="a">
            <section id="i">
                <item1 id="1" method="delete"/> <!-- leave this node -->
            </section>
            <section id="i">
            </section>
            <section id="i">
                <item1 id="1" method="create"> <!-- leave this node -->
                    <somechild>a</somechild>
                </item1>
            </section>
        </nodeA>
    </sector> 
</root>

我的想法是,我想删除元素节点与

的组合
  • 一个方法创建后跟一个或多个修改,然后是一个删除方法,其余部分保持不变。
  • 它必须与示例<item1>@id的元素名称相同,并且在同一父级下,例如<section id=1>

有人可以帮我改造吗?

感谢。 约翰

2 个答案:

答案 0 :(得分:1)

约翰,我有一个你可能喜欢或不喜欢的解决方案。我做了一个“优化”来简化一些事情。我认为既然你正在使用与相同的相同的section处理那些@id个节点,你可能也不介意在结果中将它们全部合并为一个节点文献。看看这是否可以接受。

以下 XSLT 2.0 转换:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="nodeA">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:for-each-group select="*" group-by="concat(name(), '|', @id)">
                <xsl:copy>
                    <xsl:apply-templates select="@*"/>
                    <xsl:variable name="merged">
                        <xsl:for-each-group select="current-group()/*"
                                            group-by="concat(name(), '|', @id)">
                            <xsl:copy-of select="current-group()"/>
                        </xsl:for-each-group>
                    </xsl:variable>
                    <xsl:apply-templates select="$merged/*"/>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@method = 'create']
                          [following-sibling::*[@method = 'change']
                                               [following-sibling::*[@method = 'delete']]]"/>
    <xsl:template match="*[@method = 'change']
                          [preceding-sibling::*[@method = 'create']]
                          [following-sibling::*[@method = 'delete']]"/>

    <xsl:template match="*[@method = 'delete']
                          [preceding-sibling::*[@method = 'change']
                                               [preceding-sibling::*[@method = 'create']]]"/>

</xsl:stylesheet>

应用于输入文档时会产生:

<root>
    <sector>
      <nodeA id="a">
         <section id="i">
            <item1 id="1" method="delete"/>
            <item1 id="1" method="create">
                    <somechild>a</somechild>
                </item1>
         </section>
      </nodeA>
    </sector>
</root>

我认为将这些item1节点折叠成平面有序列表会更容易,然后才会过滤掉要删除的序列。我不得不将它们复制到一个变量中以从原始文档树“分离”,否则那些sibling轴将在原始父节点内查找。一旦你有一个“相同”父节点的“相同”item1节点的平面列表,过滤创建 - 更改 - 删除序列真的是小菜一碟。我刚刚将您的规则直接转换为XPath谓词 - 一个用于create,后跟change后跟一个delete,另一个用于change,后跟create并遵循一个deletedelete的最后一个change前面有create。他们都被“知道”属于同一个name()@id并来自同一个(name()@id)父母,因此我们无需检查一部分。

答案 1 :(得分:1)

这不容易,但它适用于xslt 1.0!

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
<!--
    copies all nodes with id
    if there first preceding delete followed by the first preceding create
    -->
    <xsl:template match="*[not(.//*[@id!=''])]">
        <xsl:variable name="id" select="@id"/>
        <xsl:variable name="parentId" select="../@id"/>
        <xsl:variable name="precedings" select="preceding::*[@id=$id][../@id=$parentId]"/>
        <xsl:variable name="lastDelete" select="($precedings[@method='delete'])[last()]"/>
        <xsl:variable name="lastCreate" select="($precedings[@method='create'])[last()]"/>
        <xsl:variable name="openCreate" select="$lastDelete[following::* = $lastCreate] or (not($lastDelete) and $lastCreate)"/>
        <xsl:if test="not(following::*[@id=$id][../@id=$parentId][@method='delete'] and $openCreate)">
            <xsl:copy>
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <!--
    copies all deletes, if they have no preceding creates
    -->
    <xsl:template match="*[not(.//*[@id!=''])][@method='delete']" priority="10">
        <xsl:variable name="id" select="@id"/>
        <xsl:variable name="parentId" select="../@id"/>
        <xsl:variable name="precedings" select="preceding::*[@id=$id][../@id=$parentId]"/>
        <xsl:variable name="precCreate" select="$precedings[@method='create']"/>
        <xsl:if test="not($precCreate)">
            <xsl:copy>
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

<!--
    copies all creates, if they have no following deletes
    -->

    <xsl:template match="*[not(.//*[@id!=''])][@method='create']" priority="10">
        <xsl:variable name="id" select="@id"/>
        <xsl:variable name="parentId" select="../@id"/>
        <xsl:variable name="followings" select="following::*[@id=$id][../@id=$parentId]"/>
        <xsl:variable name="followDelete" select="$followings[@method='delete']"/>
        <xsl:if test="not($followDelete)">
            <xsl:copy>
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <!--
        copies all nodes
    -->
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

测试它......无保证!