我有这个输入:
<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>
。有人可以帮我改造吗?
感谢。 约翰
答案 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
并遵循一个delete
,delete
的最后一个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>
测试它......无保证!