Search code examples
c#xmlxml-parsingxmlnode

C# Parsing an XML and storing it into a Class


I need to parse an XML and store it into a class but it's been giving me a lot more headaches than I'm happy to admit.

Assume the XML looks like this:

<PARENT>
    <ITEM>
        <DATA1>
            <A>12345</A>
            <B>12345</B>
            <C>12345</C>
        </DATA1>
        <DATA2>
            <D>12345</D>
            <E>12345</E>
            <F>12345</F>
        </DATA2>
        <DATA3>
            <ITEM>
                <G/>
                <H>111</H>
                <I>223</I>
            </ITEM>
            <ITEM>
                <G/>
                <H>2342</H>
                <I>25323</I>
            </ITEM>
            <ITEM>
                <G>185</G>
                <H>63611</H>
                <I/>
            </ITEM>
        </DATA3>
    </ITEM>
    <ITEM>
        <DATA1>
            <A>23456</A>
            <B>23456</B>
            <C>23456</C>
        </DATA1>
    </ITEM>
</PARENT>

The first problem I'm facing is the fact that the name "ITEM" doesn't only appear as a childnode of "PARENT" but is also reused again under of "DATA3" so if I just search like this

XDocument doc = XDocument.Parse(tmp);
IEnumerable<XElement> tmp_list = doc.Descendants(XName.Get("ITEM"));

it will give me gives me five results instead of just two.

I've also already tried the approach of searching the path //PARENT/ITEM like this:

string file = @"C:\temp\test.xml";

var document = XDocument.Load(file);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("empty", "random:namespace:name");

IEnumerable<XElement> ele_list = document.XPathSelectElements("//PARENT//ITEM", namespaceManager);

but it doesn't work either since it also fetches the all the grandchild nodes...

Question 1) How can I correctly restrict the search to just the top level nodes?

Question 2) How would I then proceed and store the whole thing into a class in the most efficient way? I've tried copypasting the full XML response to websites like http://xmltocsharp.azurewebsites.net/ but all of them seem to be struggling with the duplicate name "ITEM" and basically create one class that has merged ALL of the different "ITEM" child nodes within. Is it possible to fill a List of items in a simple and efficient way? I was hoping to have essentially something along the lines of:

List<DataItem> grd_list = doc.Descendants(XName.Get("ITEM"))
   .Select(f => new DataItem()
   {
      DATA1 = f.Element(XName.Get("DATA1")).Document.ToString(),
      DATA2 = f.Element(XName.Get("DATA2")).Document.ToString(),
      //etc.....
   }).ToList();

but I'm aware that the above LINQ would have to be more complex than this since DATA1 etc also needs to have child nodes.


Solution

  • you could use serializer XML (its just an example following your xml)

    you could avoid the problem of item by defining all cases in the definition of the class ITEM. So the items not presents (G,H,I or DATA1,DATA2.. will be null).

    using System.Xml.Serialization;
    XmlSerializer serializer = new XmlSerializer(typeof(PARENT));
    using (StringReader reader = new StringReader(xml))
    {
       var test = (PARENT)serializer.Deserialize(reader);
    }
      :
      :
    [XmlRoot(ElementName = "DATA1")]
    public class DATA1
    {
    
        [XmlElement(ElementName = "A")]
        public string A { get; set; }
    
        [XmlElement(ElementName = "B")]
        public string B { get; set; }
    
        [XmlElement(ElementName = "C")]
        public string C { get; set; }
    }
    
    [XmlRoot(ElementName = "DATA2")]
    public class DATA2
    {
    
        [XmlElement(ElementName = "D")]
        public string D { get; set; }
    
        [XmlElement(ElementName = "E")]
        public string E { get; set; }
    
        [XmlElement(ElementName = "F")]
        public string F { get; set; }
    }
    
    [XmlRoot(ElementName = "ITEM")]
    public class ITEM
    {
    
        [XmlElement(ElementName = "G")]
        public string G { get; set; }
    
        [XmlElement(ElementName = "H")]
        public string H { get; set; }
    
        [XmlElement(ElementName = "I")]
        public string I { get; set; }
    
        [XmlElement(ElementName = "DATA1")]
        public DATA1 DATA1 { get; set; }
    
        [XmlElement(ElementName = "DATA2")]
        public DATA2 DATA2 { get; set; }
    
        [XmlElement(ElementName = "DATA3")]
        public DATA3 DATA3 { get; set; }
    }
    
    [XmlRoot(ElementName = "DATA3")]
    public class DATA3
    {
        [XmlElement(ElementName = "ITEM")]
        public List<ITEM> ITEM { get; set; }
    }
    
    [XmlRoot(ElementName = "PARENT")]
    public class PARENT
    {
        [XmlElement(ElementName = "ITEM")]
        public List<ITEM> ITEM { get; set; }
    }