Home > Blockchain >  Reading nested XML
Reading nested XML

Time:11-22

I have a xml file and I have to store the values in the list to use it later. The xml file contain mostly ints and strings and it looks like this:

<AllThings>
      <Anythings>
        <Anything Step="1" Name="1">
          <Somethings>
            <Something Id="10">
              <Things>
                <Thing Id="11">abc</Thing>
                <Thing Id="12">123</Thing>
              </Things>
            </Something>
            <Something Id="20">
              <Things>
                <Thing Id="21">cde</Thing>
                <Thing Id="22">345</Thing>
              </Things>
            </Something>
            <Something Id="30">
              <Things>
                <Thing Id="31">efg</Thing>
                <Thing Id="32">567</Thing>              
              </Things>
            </Something>
            <Something Id="40">
              <Things>
                <Thing Id="41">ghi</Thing>
                <Thing Id="42">789</Thing>                
              </Things>
            </Something>
    </Somethings>
    </Anything>
    <Anything Step="2" Name="2">
          <Somethings>
            <Something Id="10">
              <Things>
                <Thing Id="11">aaa</Thing>
                <Thing Id="12">111</Thing>
              </Things>
            </Something>
            <Something Id="20">
              <Things>
                <Thing Id="21">ccc</Thing>
                <Thing Id="22">333</Thing>
              </Things>
            </Something>
            <Something Id="30">
              <Things>
                <Thing Id="31">eee</Thing>
                <Thing Id="32">555</Thing>              
              </Things>
            </Something>
            <Something Id="40">
              <Things>
                <Thing Id="41">ggg</Thing>
                <Thing Id="42">777</Thing>                
              </Things>
            </Something>
        </Somethings>
        </Anything>
    </Anythings>
</AllThings>

What is the best way to get the values from this kind of xml and store it in the list(s)?

I've tried with using System.Xml; to create a reader and make reader.ReadToFollowing("Something") in combination with reader.GetAttribute("Id"), but it hasn`t gone deep enough. In most tutorials the xml is like:

<Animal type="cat">
  <Name>Bob</Name>
  <Age>8</Age>
</Animal>

and there it works

CodePudding user response:

i think you miss iterating through recursively with

.ChildNodes.OfType<XmlNode>().

CodePudding user response:

The simplest way to do this is to use XmlSerialization. To do this, you will need to create a nested class structure matching your xml. There are plenty of utilities that will do this automatically (if you search for convert xml to c# class), but I always feel, it helps to know how to construct this yourself.

I always begin with the outer shell, and work my way inwards. So I take the root element first. AllThings has just one element AnyThings, so this class is easy to create (you will need using System.Xml.Serialization;):

[XmlRoot]
public class AllThings
{

    [XmlElement]
    public Anythings Anythings { get; set; }
}

Next we look at Anythings. This holds a bunch of elements Anything, so we create the next class accordingly:

public class Anythings
{

    [XmlElement]
    public List<Anything> Anything { get; set; }
} 

Only one thing extra to note here: the bunch of Anything is being represented as a List<Anything>.

We move down again:

public class Anything
{

    [XmlElement]
    public Somethings Somethings { get; set; }

    [XmlAttribute]
    public int Step { get; set; }

    [XmlAttribute]
    public string Name { get; set; }

}

Here there is a little more of interest: as well as an XmlElement, we have two XmlAttributes. In your case the attribute Name could easily be an int, given the values in your xml, but in my view an attribute called Name is crying out to be a string!

And going on down:

public class Somethings
{

    [XmlElement]
    public List<Something> Something { get; set; }
}

public class Something
{

    [XmlElement]
    public Things Things { get; set; }

    [XmlAttribute]
    public int Id { get; set; }
}


public class Things
{

    [XmlElement]
    public List<Thing> Thing { get; set; }
}

These are all constructed in the same way as previously, leaving only one more class:

public class Thing
{

    [XmlAttribute]
    public int Id { get; set; }

    [XmlText]
    public string Text { get; set; }
}

This (at last) adds something new, a basic XmlText element which represents the value of the node.

Having set up your classes, all that remains is to call the inbuilt deserializer, like this:

var xml = "<AllThings><Anythings><Anything Step=\"1\" Name=\"1\"><Somethings><Something Id=\"10\"><Things><Thing Id=\"11\">abc</Thing><Thing Id=\"12\">123</Thing></Things></Something><Something Id=\"20\"><Things><Thing Id=\"21\">cde</Thing><Thing Id=\"22\">345</Thing></Things></Something><Something Id=\"30\"><Things><Thing Id=\"31\">efg</Thing><Thing Id=\"32\">567</Thing></Things></Something><Something Id=\"40\"><Things><Thing Id=\"41\">ghi</Thing><Thing Id=\"42\">789</Thing></Things></Something></Somethings></Anything><Anything Step=\"2\" Name=\"2\"><Somethings><Something Id=\"10\"><Things><Thing Id=\"11\">aaa</Thing><Thing Id=\"12\">111</Thing></Things></Something><Something Id=\"20\"><Things><Thing Id=\"21\">ccc</Thing><Thing Id=\"22\">333</Thing></Things></Something><Something Id=\"30\"><Things><Thing Id=\"31\">eee</Thing><Thing Id=\"32\">555</Thing></Things></Something><Something Id=\"40\"><Things><Thing Id=\"41\">ggg</Thing><Thing Id=\"42\">777</Thing></Things></Something></Somethings></Anything></Anythings></AllThings>";

var serializer = new XmlSerializer(typeof(AllThings));
using (var reader = new StringReader(xml))
{ 
    var allThings = (AllThings)serializer.Deserialize(reader);
    //do something with allThings; 
}

If you use the debugger to examine allThings, you will find that it is a c# object, with a structure exactly matching your xml. This means that you can access its values with syntax such as:

var test = allThings.Anythings.Anything[0].Somethings.Something[0].Things.Thing[0].Text;

CodePudding user response:

The following code is tested and works

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication51
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(AllThings));
            AllThings allthings = (AllThings)serializer.Deserialize(reader);
        }
    }
    public class AllThings
    {
        [XmlArray("Anythings")]
        [XmlArrayItem("Anything")]
        public List<AnyThing> anythings { get;set;}
    }
    public class AnyThing
    {
        [XmlAttribute()]
        public int Step { get;set;}
        [XmlAttribute()]
        public int Name { get; set; }

        [XmlArray("Somethings")]
        [XmlArrayItem("Something")]
        public List<Something> somethings { get; set; }
    }
    public class Something
    {
        [XmlAttribute()]
        public int Id { get; set; }

        [XmlArray("Things")]
        [XmlArrayItem("Thing")]
        public List<Thing> things { get;set;}
    }
    public class Thing
    {
        [XmlAttribute()]
        public int Id { get; set; }
        [XmlText]
        public string value { get; set; }
    }
 
}
  • Related