使用LINQ在C#中查找匹配两个元素级别的XML元素

3

我将尝试从一个xml文件中选择一个XElement,并匹配xml文档的两个“级别”。我的文件结构是:

<app>
  <Library Name="Main" Path="C:\somefile.Xml">
    <ReadingList Name="Test1">
      <Book>...
    <ReadingList Name="Test2">
      <Book>...
  <Library Name="Backup" Path="C:\somefile.Xml">

我希望能够在"Main"图书馆中找到名为"test2"的阅读列表,以便将此元素及其所有子元素复制到另一个图书馆节点。

我更喜欢使用linq的解决方案,因为我正在尝试学习它。

非常感谢您的任何帮助。

当我添加一个新的“阅读列表”时,我是这样做的:

public void AddReadingList(string fullyQualifiedPath, Library lib, string name)
{
    XDocument xdoc = XDocument.Load(fullyQualifiedPath);

    XElement library = xdoc.Element("eStack").Elements("Library")
                .Single(x => x.Attribute("Name").Value == lib.Name);

    library.Add(new XElement("ReadingList", new XAttribute("Name", name)));

    xdoc.Save(fullyQualifiedPath);                        
}

但我想执行的操作是复制这个元素和子元素。问题在于,可能会有多个具有相同名称的“library”元素,因此我需要检查库名称和读取列表名称。这样说清楚了吗?


1
你能和我们分享一下你尝试过的吗? - Gilad Green
3个回答

2

结合使用.DescendantsWhere即可达到目的:

var result = XDocument.Load(fullyQualifiedPath)
     .Descendants("Library")
         //Can replace with `FirstOrDefault` if you know there is only one
     .Where(element => element.Attribute("Name")?.Value == "Main") 
     .Descendants("ReadingList")
     .Where(element => element.Attribute("Name")?.Value == "Test2").ToList();

你也可以使用.Elements而不是Descendants,我只是更喜欢使用它来避免在途中指定app或其他任何级别。
对于c# 6.0之前的版本,您可以执行以下操作:
var result = XDocument.Load("data.xml")
     .Descendants("Library")
     .Where(element =>
     {
        var att = element.Attribute("Name");
        return att != null ? (att.Value == "Main" ? true : false) : false;
     })
     .Descendants("ReadingList")
         //Make sure to do above change here too
     .Where(element => element.Attribute("Name")?.Value == "Test2").ToList();

或者,如果创建一个方法来帮助处理它而不是重复代码:

 Func<XElement, string, string> tryGetAttributeValue = (element, attributeName) =>
 {
    var attribute = element.Attribute(attributeName);
    return attribute == null ? string.Empty : attribute.Value;
 };

 var result = XDocument.Load("data.xml")
     .Descendants("Library")
     .Where(element => tryGetAttributeValue(element,"Name") == "Main")
     .Descendants("ReadingList")
     .Where(element => tryGetAttributeValue(element, "Name") == "Test2").ToList();

您的where语句中的.Value似乎无法正常工作。您确定这是正确的吗? - cbutler
我在两个.Where部分的.Value上都遇到了“无效表达式项'.'”的问题。你有什么想法是为什么吗? - cbutler
1
@cbutler - 是的。你在用哪个版本的C#?我猜你使用的是C# 6.0之前的版本,因此?.无法使用。 - Gilad Green
1
是的,那就是问题所在。太棒了,谢谢!我非常感激你的帮助。你提供的 C# 6.0 之前的解决方案对我很有用。 - cbutler

0
你是否在寻找类似这样的东西?
        var table = XElement.Parse(@"<app>
                                        <Library Name=""Main"" Path=""C:\somefile.Xml"">
                                            <ReadingList Name=""Test1"">
                                                <Book>Book1</Book>
                                            </ReadingList>
                                            <ReadingList Name=""Test2"">
                                                <Book>Book2</Book>
                                            </ReadingList>
                                        </Library>
                                        <Library Name=""Backup"" Path=""C:\somefile.Xml""></Library>
                                    </app>");


        var readingList = table
            .Elements("Library")
            .FirstOrDefault(x => x.Attribute("Name")?.Value == "Main")
            .Elements("ReadingList")
            .FirstOrDefault(x => x.Attribute("Name")?.Value == "Test2");

这将基本上解决您获取名为Test2且在名为Test 1ReadingList下的ReadingList的查询问题。

您可以选择使用Where而不是FirstOrDefault,但这将导致所有可能匹配项的列表。

        var readingList = table
            .Elements("Library")
            .Where(x => x.Attribute("Name").Value == "Main")
            .Elements("ReadingList")
            .Where(x => x.Attribute("Name").Value == "Test2")
            .ToList(); 

选择一个子类变体将导致更少的查找过程。
        var readingList = table
            .Descendants("Library")
            .Where(x => x.Attribute("Name").Value == "Main")
            .Descendants("ReadingList")
            .Where(x => x.Attribute("Name").Value == "Test2")
            .ToList();

1
第二个 Descendants/Elements 前的 ?. 运算符是不必要的 - 它永远不会为空。如果没有找到,它将是一个 Enumerable.Empty - Gilad Green
没错,没有注意到。已修复。 - Swagata Prateek

0

你也可以执行这个查询:

var query=  xdoc.Descendants("Library")
                .Where(e=>e.Attribute("Name").Value=="Main")
                .SelectMany(e=>e.Elements("ReadingList").Where(t=>t.Attribute("Name").Value=="Test2"));

如果只有一个 Main 元素,您也可以使用 FirstOrDefault 扩展方法:
var query=  xdoc.Descendants("Library")
                .FirstOrDefault(e=>e.Attribute("Name").Value=="Main")
                .Descendants("ReadingList").Where(t=>t.Attribute("Name").Value=="Test2"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接