如何根据条件从 XML 中删除节点?

Posted

技术标签:

【中文标题】如何根据条件从 XML 中删除节点?【英文标题】:How to remove nodes from XML based on conditions? 【发布时间】:2014-11-21 22:45:43 【问题描述】:

我需要根据条件从下面的 XML 中删除重复节点。有人可以帮我修复我写的 XSLT 吗?或者建议一个解决方法?

我的要求:如果满足以下条件,则删除整个节点。

    如果员工 ID 有重复条目 如果上述条件为“真”,则保留“类型”为“员工”的 Worker 节点。其他具有相同员工 ID 的重复“工人”节点条目将“类型”作为“条件”。

XML 文件:

 <?xml version="1.0" encoding="UTF-8"?>
 <Workers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Header>
        <File>22.0</File>
        <Date>2014-05-31T16:20:07.000-07:00</Date>
        <Worker_Count>2</Worker_Count>
    </Header>
    <Worker>
        <Summary>
            <Employee_ID>12345800</Employee_ID>
            <Name>John Davis (12345800)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>12345800</Employee_ID>
            <Name>John Davis (12345800)</Name>
            <Type>Contingent</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>32451854</Employee_ID>
            <Name>Felix (32451854)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>23471732</Employee_ID>
            <Name>David (23471732)</Name>
            <Type>Contingent</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>38741297</Employee_ID>
            <Name>Sam Daniel (38741297)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>38741297</Employee_ID>
            <Name>Sam Daniel (38741297)</Name>
            <Type>Contingent</Type>
        </Summary>
    </Worker>
</Workers>

上面的XML需要如下转换。

<?xml version="1.0" encoding="UTF-8"?>
<Workers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Header>
        <File>22.0</File>
        <Date>2014-05-31T16:20:07.000-07:00</Date>
        <Worker_Count>2</Worker_Count>
    </Header>
    <Worker>
        <Summary>
            <Employee_ID>12345800</Employee_ID>
            <Name>John Davis (12345800)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>32451854</Employee_ID>
            <Name>Felix (32451854)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>23471732</Employee_ID>
            <Name>David (23471732)</Name>
            <Type>Contingent</Type>
        </Summary>
    </Worker>
    <Worker>
        <Summary>
            <Employee_ID>38741297</Employee_ID>
            <Name>Sam Daniel (38741297)</Name>
            <Type>Employee</Type>
        </Summary>
    </Worker>
</Workers>

我在 XSLT 下面写过。不知道如何在下面的 XSLT 中添加条件以删除包含重复员工 ID 的节点,其中类型为“Contingent”

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ws="urn:com.workday/workersync">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/Workers/Worker[Summary/Type='Contingent']"/>
</xsl:stylesheet>

在 XSLT 之上删除所有具有“条件”值的类型。但是,我需要的是仅当“Employee id”在 XML 中有重复条目时才删除类型为 Contingent 的节点?

【问题讨论】:

【参考方案1】:

考虑使用键通过Employee_ID 查找Worker 元素

<xsl:key name="Worker" match="Worker" use="Summary/Employee_ID" />

这意味着您可以编写模板匹配以删除 Worker 元素:

<xsl:template match="Worker[Summary/Type='Contingent'][count(key('Worker', Summary/Employee_ID)) > 1]"/>

或者可能是这样(即检查是否有第二个Worker 具有相同的Employee_ID

<xsl:template match="Worker[Summary/Type='Contingent'][key('Worker', Summary/Employee_ID)[2]]"/>

试试这个 XSLT

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

    <xsl:key name="Worker" match="Worker" use="Summary/Employee_ID" />

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

     <xsl:template match="Worker[Summary/Type='Contingent'][key('Worker', Summary/Employee_ID)[2]]"/>
</xsl:stylesheet>

注意,不需要匹配完整的/Workers/Worker 路径。只有在您的 XML 中有不同级别的 Worker 元素时,您才真正需要这样做。

【讨论】:

这个解决方案在处理大输入数据时比我的要快。最重要的是,它更易于阅读。【参考方案2】:

在虚拟模板的匹配表达式中添加另一个谓词(此处为:[Summary/Employee_ID = preceding-sibling::Worker/Summary/Employee_ID or Summary/Employee_ID = following-sibling::Worker/Summary/Employee_ID])。

以下模板产生所需的输出:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/Workers/Worker[Summary/Type='Contingent'][Summary/Employee_ID = preceding-sibling::Worker/Summary/Employee_ID or Summary/Employee_ID = following-sibling::Worker/Summary/Employee_ID]"/>
</xsl:stylesheet>

如果谓词以这种方式链接,则所有谓词都必须满足才能进行匹配,就像在逻辑 AND 中一样。

除了添加额外的谓词之外,我还将 XSLT 版本更改为 1.0,因为该模板不使用 2.0 版本的功能。此外,我删除了不必要的命名空间声明。

【讨论】:

以上是关于如何根据条件从 XML 中删除节点?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 从 XML 节点列表中删除节点

如何从xml中删除列表父节点?

从 XML 中删除具有特定值的节点

如果xml节点没有子节点,如何删除它

如何根据节点值使用php删除xml中的元素[重复]

如何从xml中删除节点?