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

时间:2019-12-10 10:07:38

标签: c# xml linq-to-xml

我有一个复杂的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>的值必须在number元素的属性<wForm>中找到。因此,<vElement><wForm>元素配对。

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

  • 如果元素<vElement>包含子元素<elB>,则将删除该子元素。同样,成对的<wForm>元素也不会被删除。
  • 如果元素<vElement>不包含子元素<elB>,而<wForm>的元素不包含子元素<kB>,则会将其删除
  • 如果元素<vElement>不包含子元素<elB>,但成对的<wForm>包含子元素<kB>,则将删除。
  • 一个<wForm>元素可以与多个<vElement>成对。

为了更清楚地说明,我将从上面的xml代码示例中给您介绍,必须删除哪些元素:

  • 元素<vElement number="0">不能删除,因为它包含子元素<elB></elB>。同样,元素<wForm number="0">也不会被删除,因为它与此<vElement number="0">设置的<wFormNum>0</wformNum>配对。
  • 可以删除元素<vElement number="1">,因为它不包含子元素<elB></elB>,但与<wForm number="1">配对,由<wFormNum>1</wformNum>设置,而{{1} }包含类型<wForm number="1">的元素。因此,<kB><vElement number="1">将不会被删除。
  • 元素<wForm number="1"><vElement number="2">相同,因此不会被删除。
  • 元素<vElement number="0"><vElement number="3">相同,因此不会被删除,并且<vElement number="1">也不会被删除。
  • 元素<wForm number="2">将被删除,因为它不包含任何<vElement number="4">子元素,并且<elB></elB>不包含<wForm number="3">子元素。 <kB></kB>也将被删除,因为它不包含<wForm number="3">个孩子,并且与其他无法删除的<kB>不成对。

如果可能的话,我想使用Linq从一行代码中删除“可移动”节点(<vElement><vElement>)。我尝试使用多个<wForm>指令,但是很复杂,因为那些“包含或不包含子元素”的东西:(

谢谢。

3 个答案:

答案 0 :(得分:0)

这是我的解决方案。我更新了代码。 vElement不包含elb,因此您对vElement 1和wForm 1没有被删除的评论是错误的。以下内容被删除:(1)vElement 3&4(2)wForm3。我正在使用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; }
    }
}

答案 1 :(得分:0)

我不投票作为答案,因为它与我尝试发现的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;
}

答案 2 :(得分:0)

因此,据我所知,您的规则是删除满足以下条件的<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;
}