删除多个 XML 节点,如果其他一些节点不包含特定的子节点

Removing multiple XML Nodes, if some other nodes does not contains specific childs

我有一个复杂的 xml 看起来像这样:

<rootElement>
    <vElement number="0">
        <!-- Next node, elB, could or could not appear -->
        <elB>SomeData</elB>
        <block>
            <wFormNum>0</wFormNum>
        </block>
    </vElement>
    <vElement number="1">
        <block>
            <wFormNum>1</wFormNum>
        </block>
    </vElement>
    <vElement number="2">
        <!-- Next node, elB, could or could not appear -->
        <elB>SomeData</elB>
        <block>
            <wFormNum>0</wFormNum>
        </block>
    </vElement>
    <vElement number="3">
        <block>
            <wFormNum>2</wFormNum>
        </block>
    </vElement>
    <vElement number="4">
        <block>
            <wFormNum>3</wFormNum>
        </block>
    </vElement>

    .
    .
    .

    <wForm number="0">
    </wForm>
    <wForm number="1">
        <kB number="0"></kB>
        <kB number="1"></kB>
        <kB number="2"></kB>
        <kB number="3"></kB>
    </wForm>
    <wForm number="2">
        <kB number="0"></kB>
        <kB number="1"></kB>
        <kB number="2"></kB>
        <kB number="3"></kB>
        <kB number="4"></kB>
        <kB number="5"></kB>
        <kB number="6"></kB>
        <kB number="7"></kB>
    </wForm>
    <wForm number="3">
    </wForm>
</rootElement>

如您所见,元素 <vElement> 包含一个子元素 <block>,它还有子元素 <wFormNum><wFormNum> 的值必须在 <wForm> 元素的属性 number 中找到。所以 <vElement><wForm> 元素成对。

如果满足某些条件,我必须从此 xml 中删除一些 <vElement><wForm> 元素。条件如下:

所以为了更清楚,我将根据上面的xml代码示例给出您必须删除哪些元素:

如果可能,我想使用 Linq 从单行代码中删除 "removable" 个节点(<vElement><wForm>)。我尝试了多个 foreach() 指令,但是很复杂,因为那些 "containing or not containing child elements" 东西 :(

谢谢。

这是我的解决方案。我更新了代码。 vElement 不包含 elb,因此您对 vElement 1 和 wForm 1 未被删除的评论是错误的。以下内容被删除:(1) vElement 3 & 4 (2) wForm 3。我正在使用 xml linq。

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication9
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            List<WForm> wforms = doc.Descendants("wForm")
                .Select(x => new WForm() { number = (int)x.Attribute("number"), element = x })
                .ToList();

            var results = (from vElement in doc.Descendants("vElement")
                           join wForm in wforms on (int)vElement.Attribute("number") equals wForm.number into v
                           from wForm in v.DefaultIfEmpty()
                           select new { vElement = vElement, wForm = wForm }
                          ).ToList();

            foreach (var result in results)
            {
                if (result.vElement.Element("elB") != null)
                {
                    if (result.wForm != null) result.wForm.delete = false;
                    continue;
                }
                else
                {
                    if (result.wForm != null)
                    {
                        if (result.wForm.element.Element("kB") != null)
                        {
                            result.wForm.delete = false;
                        }
                        continue;
                    }
                    else
                    {
                        int number = (int)result.vElement.Attribute("number");
                        Console.WriteLine("Remove vElement : '{0}'", number);
                        result.vElement.Remove();
                    }
                }
            }
            foreach (WForm wForm in wforms)
            {
                if (wForm.delete)
                {
                    Console.WriteLine("Remove wForm : '{0}'", wForm.number);
                    wForm.element.Remove();
                }
            }
            Console.ReadLine();
        }
    }
    public class WForm
    {
        public int number { get; set; }
        public bool delete = true;
        public XElement element { get; set; }
    }
}

我不将此作为答案,因为它与我尝试发现的 LINQ 版本不同,具有简短而漂亮的可见代码,但这是我要做和工作的,并尊重声明:

private static void RemoveEmptyWForms(string filePath, string vN, string rPN) {
        XDocument xml = XDocument.Load(filePath);
        List<wForm> listWForms = new List<wForm>();
        List<Elements> listElements = new List<Elements>();
        if (xml.Descendants("vElement").Count() == 0)
        {
            Console.WriteLine("File does not contains elements of type \"vElement\"!");
            return;
        }
        else
        {
            foreach (XElement xe in xml.Descendants("vElement"))
            {
                Elements el = new Elements();
                el.PNV = rPN + " | " + vN;
                el.Node = xe;
                el.Value = xe.Attribute("number").Value;
                listElements.Add(el);
            }
        }
        foreach (XElement xw in xml.Descendants("wForm"))
        {
            string value = xw.Attribute("number").Value;
            wForm wfn = new wForm();
            wfn.PNV = rPN + " | " + vN;
            wfn.Node = xw;
            wfn.Value = value;
            if (xw.Descendants("kB").Count() > 0)
            {
                wfn.CanBeDeleted = false;
                listWForms.Add(wfn);
                continue;
            }
            wfn.CanBeDeleted = true;
            listWForms.Add(wfn);
            foreach (XElement xe in xml.Descendants("vElement")) {
                if (xe.Descendants("elB").Count() > 0)
                {
                    foreach (Elements el in listElements)
                    {
                        if (el.Node != xe) continue;
                        else { 
                            el.CanBeDeleted = false;
                            break;
                        }
                    }
                }
                else {
                    string xeWfn = xe.Element("block").Element("wFormNum").Value;
                    foreach (wForm wf in listWForms) {
                        if (wf.Value.Equals(xeWfn))
                        {
                            if (wf.CanBeDeleted == false) 
                            {
                                foreach (Elements el in listElements)
                                {
                                    if (el.Node != xe) continue;
                                    else
                                    {
                                        el.CanBeDeleted = false;
                                        break;
                                    }
                                }
                                break;
                            }
                            foreach (Elements el in listElements)
                            {
                                if (el.Node != xe) continue;
                                else
                                {
                                    el.CanBeDeleted = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        foreach (wForm wf in listWForms) {
            if (wf.CanBeDeleted) { 
                wf.Node.Remove();
                Console.WriteLine("Removing wForm {0} from v {1}", wf.Value, wf.PNV);
            }
        }
        foreach (Elements el in listElements) {
            if (el.CanBeDeleted) { 
                el.Node.Remove();
                Console.WriteLine("Removing element {0} from v {1}", el.Value, el.PNV);
            }
        }
    }

class WaveForm { 
    public string PNV { get; set; }
    public XNode Node { get; set; }
    public string Value { get; set; }
    public bool CanBeDeleted { get; set; } = true;
}
class Elements {
    public string PNV{ get; set; }
    public XNode Node { get; set; }
    public string Value { get; set; }
    public bool CanBeDeleted { get; set; } = true;
}

因此,据我了解,您的规则是删除满足以下条件的 <vElement> 个节点:

  1. <vElement> 不包含任何 <elB> 个子节点。
  2. <vElement> 没有相应的 <wForm> 非空元素。
XElement Cleanup(XElement element)
{
    var clean = new XElement(element);
    var nonEmptyForms = clean.XPathSelectElements("//wForm[*]")
        .Select(f => (int)f.Attribute("number"))
        .ToHashSet();
    clean.XPathSelectElements("//vElement")
        .Where(e => !e.Elements("elB").Any())
        .Where(e => !nonEmptyForms.Contains((int)e.XPathSelectElement("block/wFormNum")))
        .Remove();
    return clean;
}