dertrautmann
dertrautmann

Reputation: 73

XML-Deserialization of a nested List C#

I am trying to deserialize a Xml File using the XmlSerializer. A part of my file looks like this:

<bla>
    <ListOfListOfTest>
         <ListOfTest> 
               <Test>       
               </Test>        
        </ListOfTest>        
    </ListOfListOfTest>
</bla>

I tried different ways but it does not work.

My first try looked like:

public class bla
    {
        public bla()
        {
            ListOfListOfTest = new List<List<Test>>();
        }
        [...]
        public List<List<Test>> ListOfListOfTest{ get; set; }            
    }

-> does not work.

Second try:

public class bla
    {
        public bla()
        {
            ListOfListOfTest = new List<List<Test>>();
        }
        [..]
        public List<List<Test>> ListOfListOfTest { get; set; }

        [XmlArrayItemAttribute]
        public List<List<Test>> listOfListOfTest { get { return ListOfListOfTest ; } }
    }

-> failed as well

Third try:

public class bla
    {
        public bla()
        {
            ListOfListOfTest = new List<Foo>();
        }
        [...]
        public List<Foo> ListOfListOfTest { get; set; }

    }


    public class Foo
    {
        public Foo()
        {
            ListOfTest = new List<Test>();   
        }
        public List<Test> ListOfTest { get; set; }
    }

-> failed...

Failed means that the XmlSerializer does not fill the List during serializer.Deserialize(). I´m always getting a List with zero Elements in it.

What am i doing wrong?

thanks for your effort

Upvotes: 2

Views: 2272

Answers (3)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236248

Visual Studio has a handy option - you should just copy-paste your xml and go to menu Edit > Paste Special > Paste XML As Classes. And Visual Studio will generate classes which you can use to serialize/deserialize your xml. In this particular case it will generate:

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class bla
{
    private blaListOfListOfTest listOfListOfTestField;

    public blaListOfListOfTest ListOfListOfTest
    {
        get { return this.listOfListOfTestField; }
        set { this.listOfListOfTestField = value; }
    }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class blaListOfListOfTest
{
    private blaListOfListOfTestListOfTest listOfTestField;

    public blaListOfListOfTestListOfTest ListOfTest
    {
        get { return this.listOfTestField; }
        set { this.listOfTestField = value; }
    }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class blaListOfListOfTestListOfTest
{
    private object testField;

    public object Test
    {
        get { return this.testField; }
        set { this.testField = value; }
    }
}

You can do some adjustments after that - e.g. remove type qualifiers or replace properties with auto-properties (which can be done with visual studio extensions). After few keystrokes:

[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public partial class bla
{
    public blaListOfListOfTest ListOfListOfTest { get; set; }
}

[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class blaListOfListOfTest
{
    public blaListOfListOfTestListOfTest ListOfTest { get; set; }
}

[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class blaListOfListOfTestListOfTest
{
    public object Test { get; set; }
}

And deserialization will look like:

var serializer = new XmlSerializer(typeof(bla));
var blaObj = serializer.Deserialize(new StringReader(xmlString));

Upvotes: 1

Christopher Lake
Christopher Lake

Reputation: 378

To get to grips with how the XML will look when correctly deserialized, copy your XML:

<bla>
    <ListOfListOfTest>
         <ListOfTest> 
               <Test>       
               </Test>        
        </ListOfTest>        
    </ListOfListOfTest>
</bla>

Create a class in C#. Click "Edit" at the top, then "Paste Special" then "Paste XML As Classes" while your cursor is inside the class. Visual Studio will correctly deserialize the XML for you and create the necessary classes. Use this to compare what you thought you needed, and what you actually need, in order to clarify for yourself how deserialization should work.

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1062915

Something like this?

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

class Program {
    static void Main() {
        var xml = @"<bla>
    <ListOfListOfTest>
         <ListOfTest> 
               <Test>       
               </Test>        
        </ListOfTest>        
    </ListOfListOfTest>
</bla>";
        var bar = (Bar)new XmlSerializer(typeof(Bar)).Deserialize(new StringReader(xml));
        Console.WriteLine(bar.Lists.Sum(_ => _.Items.Count)); // 1
    }
}
[XmlRoot("bla")]
public class Bar {
    [XmlArray("ListOfListOfTest")]
    [XmlArrayItem("ListOfTest")]
    public List<Foo> Lists { get; } = new List<Foo>();
}
public class Foo {
    [XmlElement("Test")]
    public List<Test> Items { get; } = new List<Test>();
}
public class Test { }

The actual layout depends on which elements might be duplicated, and whether you need to be able to reproduce the exact organisation (vs just wanting all the Test items). In the code above, ListOfListOfTest is not expected to be duplicated, but there can be any number of ListOfTest or Test elements.

Upvotes: 3

Related Questions