Search code examples
c#xmllinq

Looping through items and read desired elements


I have something like this:

<ITEMS ASOF_DATE="6/2/2022" RECORDS="1" CREATE_DATE="6/3/2022" >
    <ITEM>
        <port_name>95512M</port_name>
        <bench_name>LEHSECUR</bench_name>
        <SomeValue>-808</SomeValue>
    </ITEM>
    <ITEM>
        <port_name>95512M</port_name>
        <bench_name>LEHSECUR</bench_name>
        <SomeValue>-808</SomeValue>
        <SomeOtherValue>-808</SomeOtherValue>
    </ITEM> 
    <ITEM>
        <port_name>95512M</port_name>
        <bench_name>LEHSECUR</bench_name>
        <SomethingElse>234</SomethingElse>
    </ITEM> 
</ITEMS>

It can have multiple <ITEM> items and those can have multiple elements under it for example first item has three elements, second one has four , third one has three in this example. I want to loop through all of these <ITEM> items and read some specific elements that I am interested in. For example <port_name> and <bench_name>

But I can't figure out how to do the loop. For example root.SelectNodes("ITEMS") in my code below isn't what I am hoping to be able to loop through its items.

 XmlDocument doc = new XmlDocument();
        doc.Load(myFilePath.ToString());
        XmlElement root = doc.DocumentElement;
        var lineItems = root.InnerXml;

        XmlNodeList nodes = root.SelectNodes("ITEMS");
        foreach (XmlNode node in nodes)
        {
            var somethingElse = node.SelectNodes("ITEM");
        }

Solution

  • Try using the Elements() or Descendants() methods to iterate your root element.

    Edited in response to comment:

    If you're looking for something specific, I have found that one of the easiest ways to do this is to write an Extension Method for XElement that suites your requirements. Here is an example.

    static class Extensions
    {
        public static bool IsPortNameMatch(this XElement xElement, string name)
        {
            if (!string.Equals(xElement.Name.LocalName, "ITEM")) return false;
            var portName = xElement.Element("port_name");
            if (portName == null) return false;
            return string.Equals((string)portName, name);
        }
    }
    

    To test the extension, I have added a fourth ITEM to the source with a port_name of "UniquePortName". The new console output now reports this as "Found".

    static void Main(string[] args)
    {
        var root = XElement.Parse(source);
        foreach (var item in root.Elements("ITEM"))
        {
            foreach (var entry in item.Elements())
            {
                Console.WriteLine($"{entry.Name.LocalName} = {(string)entry}");
            }
        }
        // Find something specific
        var matchItem =
            root.Descendants("ITEM")
            .First(match => match.IsPortNameMatch("UniquePortName"));
        Console.WriteLine($"FOUND:{Environment.NewLine}{matchItem.ToString()}");
    
    }
    const string source =
    @"<ITEMS ASOF_DATE=""6/2/2022"" RECORDS=""1"" CREATE_DATE=""6/3/2022"" >
        <ITEM>
            <port_name>95512M</port_name>
            <bench_name>LEHSECUR</bench_name>
            <SomeValue>-808</SomeValue>
        </ITEM>
        <ITEM>
            <port_name>95512M</port_name>
            <bench_name>LEHSECUR</bench_name>
            <SomeValue>-808</SomeValue>
            <SomeOtherValue>-808</SomeOtherValue>
        </ITEM> 
        <ITEM>
            <port_name>95512M</port_name>
            <bench_name>LEHSECUR</bench_name>
            <SomethingElse>234</SomethingElse>
        </ITEM>  
        <ITEM>
            <port_name>UniquePortName</port_name>
            <bench_name>LEHSECUR</bench_name>
            <SomethingElse>234</SomethingElse>
        </ITEM> 
    </ITEMS>";
    }
    

    console output