c# - 如何根据多个父元素的属性删除子元素?

标签 c# xml linq-to-xml

我有一个类似于以下结构的 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<Root Attr1="Foo" Name="MyName" Attr2="Bar" >
    <Parent1 Name="IS">
        <Child1 Name="Kronos1">
            <GrandChild1 Name="Word_1"/>
            <GrandChild2 Name="Word_2"/>
            <GrandChild3 Name="Word_3"/>
            <GrandChild4 Name="Word_4"/>
        </Child1>
        <Child2 Name="Kronos2">
            <GrandChild1 Name="Word_1"/>
            <GrandChild2 Name="Word_2"/>
            <GrandChild3 Name="Word_3"/>
            <GrandChild4 Name="Word_4"/>
        </Child2>
    </Parent1>
</Root>

元素未定义,因为它们可以具有与其他文件不同的值。我所知道的是手头每个元素的“名称”属性,它将始终被定义。我需要能够根据该名称操作和/或删除所选元素中的数据。例如:removeElement("MyName.IS.Kronos1.Word_1") 将删除 Child1 父项下的 GrandChild1 元素。

我的问题是,在使用 LINQ to XML 查询时,我无法正确选择该元素。使用这个:

private IEnumerable<XElement> findElements(IEnumerable<XElement> docElements, string[] names)
{
    // the string[] is an array from the desired element to be removed.
    // i.e. My.Name.IS ==> array[ "My, "Name", "IS"]
    IEnumerable<XElement> currentSelection = docElements.Descendants();

    foreach (string name in names)
    {
        currentSelection =
            from el in currentSelection
            where el.Attribute("Name").Value == name
            select el;
    }

    return currentSelection;

}

要找到我需要删除元素的位置会产生以下结果:

<?xml version="1.0" encoding="utf-8"?>
<Root Attr1="Foo" Name="MyName" Attr2="Bar" >
    <Parent1 Name="IS">
        <Child1 Name="Kronos1">
            <GrandChild2 Name="Word_2"/>
            <GrandChild3 Name="Word_3"/>
            <GrandChild4 Name="Word_4"/>
        </Child1>
        <Child2 Name="Kronos2">
            <GrandChild2 Name="Word_2"/>
            <GrandChild3 Name="Word_3"/>
            <GrandChild4 Name="Word_4"/>
        </Child2>
    </Parent1>
</Root>

调试后,似乎我所做的只是再次搜索同一个文档,但每次搜索的名称不同。如何根据多个父属性名称搜索和选择特定元素?

应注意,XML 的大小(表示元素的级别)也是可变的。这意味着可以有少至 2 个级别( parent )或最多 6 个级别(曾孙)。但是,我还需要能够查看根节点的 Name 属性。

最佳答案

这应该有效:

if (doc.Root.Attribute("Name").Value != names.First())
    throw new InvalidOperationException("Sequence contains no matching element.");

var selection = doc.Root;

foreach (var next in names.Skip(1))
    selection = selection.Elements().First(x => x.Attribute("Name").Value == next);

return selection;

如果您愿意,可以用以下内容替换最新的行:

var selection = names.Skip(1).Aggregate(doc.Root, (current, next) => current.Elements().First(x => x.Attribute("Name").Value == next));

如果在源中找不到匹配的元素,.First() 方法会抛出异常。


最干净的方法是添加一个新函数:

XElement SelectChildElement(XElement current, string child)
{
    if (current == null)
        return null;        

    var elements = current.Elements();
    return elements.FirstOrDefault(x => x.Attribute("Name").Value == child);
}

这样你就可以简单地使用它如下:

if (doc.Root.Attribute("Name").Value != names.First())
    return null;

return names.Skip(1).Aggregate(doc.Root, SelectChildElement);

然后,如果您需要选择一个 child ,您可以使用方便的 SelectChildElement() 方法。如果您想改为执行 myElement.SelectChild(child),您可以从 extension 中调用它.

此外,当您在此处使用 FirstOrDefault 时,您不会得到异常,而是返回 null

这样,它就不必跟踪通常代价更高的异常......

关于c# - 如何根据多个父元素的属性删除子元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10889071/

相关文章:

c# - 将 List<int?> 转换为 List<int> 的任何简单方法

c# - 在 LINQ 中,选择属性 X 的所有值,其中 X != null

android - Android 中相当于 WPF Grid 的控件

javascript - Nativescript Canvas

c# - 读取 XML 文档时如何使用 XComment?

c# - 使用 XElement 的方法 Elements(XName) 的 Linq To Xml 问题

c# - linq 到 xml。读。并分配给 ViewData..noob

c# - ASP.NET 身份发送电子邮件 : TaskCanceledException

c# - 为泛型类中的静态方法设置无类型

xml - 为什么在第一个应用模板中没有选择