Mason Wheeler
Mason Wheeler

Reputation: 84590

How to find an XML node that is missing a specific child node?

This MSDN page shows how to use Linq-to-XML to find XML nodes that contain a certain child node. Unfortunately, I have the opposite problem: I have a large list and a few of those nodes are missing a certain child node that should be there. Is there any good way to find them?

For example:

<Objects>
  <Object>
    <ID>1</ID>
    <A>1</A>
    <B>1</B>
    <C>1</C>
  </Object>
  <Object>
    <ID>2</ID>
    <A>2</A>
    <B>2</B>
    <C>2</C>
  </Object>
  <Object>
    <ID>3</ID>
    <A>3</A>
    <C>3</C>
  </Object>
  <Object>
    <ID>4</ID>
    <A>4</A>
    <B/>
    <C>4</C>
  </Object>
</Objects>

How would I set up code to find all <Object> elements with a missing <B> node, which would return #3, but not #4?

Upvotes: 3

Views: 2097

Answers (2)

Cylian
Cylian

Reputation: 11182

Here is another work-around, if you like to utilize the XPath for this case, you may use the below approach also:

static void Main(string[] args)
        {
            XElement objects = XElement.Parse(@"<Objects>
                                                  <Object>
                                                    <ID>1</ID>
                                                    <A>1</A>
                                                    <B>1</B>
                                                    <C>1</C>
                                                  </Object>
                                                  <Object>
                                                    <ID>2</ID>
                                                    <A>2</A>
                                                    <B>2</B>
                                                    <C>2</C>
                                                  </Object>
                                                  <Object>
                                                    <ID>3</ID>
                                                    <A>3</A>
                                                    <C>3</C>
                                                  </Object>
                                                  <Object>
                                                    <ID>4</ID>
                                                    <A>4</A>
                                                    <B/>
                                                    <C>4</C>
                                                  </Object>
                                                </Objects>");

            string xpath_string = "//Object[count(B) = 0]"; //you can change the name of the element anytime,
                                                            //even in the run-time also... just make sure
                                                            //to add `System.Xml.XPath` to utilize the XPath...

            IEnumerable<XElement> query_for_objects = objects.XPathSelectElements(xpath_string);

            foreach (XElement ele in query_for_objects)
            {
                Console.WriteLine(ele.ToString());
            }
            Console.ReadKey();

What is nice using XPath for this case, you may use a customized query even at run-time also, without changing your code at all!

Upvotes: 2

Quantic
Quantic

Reputation: 1799

This works because XContainer.Element("elementName") returns null if elementName doesn't exist (note: I copied your xml into a string called xml, so you need to just do .Descendents on your XDocument):

    static void Main(string[] args)
    {
        var elementsWithoutB = XDocument.Parse(xml)
                               .Descendants("Object")
                               .Where(x => x.Element("B") == null);

        // Prove it works by printing the elements returned
        foreach (var element in elementsWithoutB)
        {
            Console.WriteLine(element.ToString());
        }

        Console.Read();
    }

Upvotes: 2

Related Questions