简体   繁体   中英

XDocument: How to find an attribute value in nested xml

Given the working code snippet below, I'm trying to get the target element inside RootChild2 .

https://dotnetfiddle.net/eN4xV9

string str =
    @"<?xml version=""1.0""?>  
    <!-- comment at the root level -->  
    <Root>  
        <RootChild1>
            <Child>Content</Child>  
            <Child>Content</Child>  
            <Child>Content</Child>  
        </RootChild1>
        <RootChild2>
            <Child>Content</Child>  
            <Child key='target'>Content</Child>  
            <Child>Content</Child>  
        </RootChild2>
    </Root>";
XDocument doc = XDocument.Parse(str);

foreach (XElement element in doc.Descendants("RootChild2"))
{
    if (element.HasAttributes && element.Element("Child").Attribute("key").Value == "target")
        Console.WriteLine("found it");
    else
        Console.WriteLine("not found");
}

This finds all 3 RootChild2/Child elements, then tests each in turn:

        foreach (XElement child in doc.Descendants("RootChild2").Elements("Child"))
        {
            if ((string)child.Attribute("key") == "target")
                Console.WriteLine("found it");
            else
                Console.WriteLine("not found");
        }

There are three problems here:

  • You're checking if the RootChild2 element has any attributes - and it doesn't
  • You're only checking the first Child element under each RootChild2 element
  • You're assuming the attribute is present (by dereferencing the XAttribute )

Here's code which will find all the target elements within a RootChild2 :

foreach (XElement element in doc.Descendants("RootChild2"))
{
    var targets = element
        .Elements("Child")
        .Where(child => (string) child.Attribute("key") == "target")
        .ToList();
    Console.WriteLine($"Found {targets.Count} targets");
    foreach (var target in targets)
    {
        Console.WriteLine($"Target content: {target.Value}");
    }            
}

Note that the cast of XAttribute to string is a simple way of avoiding null reference issues - because the result of the explicit conversion is null when the source is null. (That's a general pattern in LINQ to XML.)

You are accessing the RootChild2-Element itself through element inside your loop. Take a look at the following version:

        foreach (XElement element in doc.Descendants("RootChild2").Nodes())
        {
            if (element.HasAttributes && element.Attribute("key").Value == "target")
                Console.WriteLine("found it");
            else
                Console.WriteLine("not found");
        }

Now it loops through all nodes of RootChild2.

You should rewrite your foreach loop at little bit, add accessing of child Elements() collection in RootChild2 and check every element in enumeration. You also can check that key attribute is present in element to prevent a potential null reference exception

foreach (XElement element in doc.Descendants("RootChild2").Elements())
{
    if (element.HasAttributes && element.Attribute("key")?.Value == "target")
        Console.WriteLine("found it");
    else
        Console.WriteLine("not found");
}

Descendants(XName) returns only elements with matching names, therefore you are getting only one RootChild2 element as the result in your code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM