Xml 如何修复XSLT中的这种消除转换?

Xml 如何修复XSLT中的这种消除转换?,xml,xslt,Xml,Xslt,我有以下意见: <root> <sector> <nodeA id="a"> <section id="i"> <item1 id="1" method="delete"/> <item1 id="1" method="create"> <somechild>

我有以下意见:

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

A.
A.
A.
A.
我的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>

A.
我想删除元素与节点的组合

  • 一个方法创建,然后是一个或多个修改,然后是一个删除方法,其余方法保持不变
  • 它必须是相同的元素名称,例如
    @id
    ,并且必须位于相同的父项下,例如
谁能帮我改造一下吗

谢谢。
约翰

约翰,我有一个解决方案,你可能喜欢,也可能不喜欢。我做了一个“优化”来简化一些事情。我认为,既然您将那些具有相同
@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>

A.

我认为将那些
item1
节点折叠成一个简单有序的列表,然后过滤掉想要删除的序列会容易得多。我必须将它们复制到一个变量中,以便从原始文档树中“分离”,否则那些
同级轴将在原始父节点中查看。一旦您拥有来自“相同”父节点的“相同”
item1
节点的平面列表,过滤create-change-delete序列真是小菜一碟。我刚刚将您的规则直接转换为XPath谓词-一个用于
create
,后面是
change
,后面是
delete
,另一个用于
change
,前面是
create
,后面是
delete
,最后一个是
delete
,前面是
change
,前面是
create
。它们都“已知”为相同的
name()
@id
,并且来自相同的(
name()
@id
)父级,因此我们不必检查该部分。

这并不容易,但它在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>


测试它。。。没有保证

你的问题变得越来越棘手:)它有效,但我相信它缺少一个要求。那些属于
[@id=$id]
item1
节点也应该属于“同一”父节点,才有资格进行“合并”。类似于
[parent::*/@id=(current()/parent:::*/@id)]
的东西需要添加到那些
[@id=$id]
谓词中。然后我使用了
创建
-
更改
-
删除
序列的各种组合,结果与John的要求不匹配。我想你的转换需要更多的测试和调整,但你肯定在这方面有所进展。好吧,我做了一些改变:我误解了第二个要求,我修正了它。还有一些错误。谢谢你的解决方案。我仍在尝试实现它以匹配更大的xml输入。我会再打给你的。再次感谢