Jon Artus
Jon Artus

Reputation: 6358

Serializing Lists of Classes to XML

I have a collection of classes that I want to serialize out to an XML file. It looks something like this:

public class Foo
{
  public List<Bar> BarList { get; set; }
}

Where a bar is just a wrapper for a collection of properties, like this:

public class Bar
{
  public string Property1 { get; set; }
  public string Property2 { get; set; }
}

I want to mark this up so that it outputs to an XML file - this will be used for both persistence, and also to render the settings via an XSLT to a nice human-readable form.

I want to get a nice XML representation like this:

<?xml version="1.0" encoding="utf-8"?>
<Foo>
  <BarList>
    <Bar>
      <Property1>Value</Property1>
      <Property2>Value</Property2>   
    </Bar>
    <Bar>
      <Property1>Value</Property1>
      <Property2>Value</Property2>   
    </Bar>
  </Barlist>
</Foo>

where are all of the Bars in the Barlist are written out with all of their properties. I'm fairly sure that I'll need some markup on the class definition to make it work, but I can't seem to find the right combination.

I've marked Foo with the attribute

[XmlRoot("Foo")]  

and the list<Bar> with the attribute

[XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName="Bar")]

in an attempt to tell the Serializer what I want to happen. This doesn't seem to work however and I just get an empty tag, looking like this:

<?xml version="1.0" encoding="utf-8"?>
<Foo> 
  <Barlist />
</Foo>

I'm not sure if the fact I'm using Automatic Properties should have any effect, or if the use of generics requires any special treatment. I've gotten this to work with simpler types like a list of strings, but a list of classes so far eludes me.

Upvotes: 36

Views: 88595

Answers (4)

var xmlfromLINQ = new XElement("BarList",
            from c in BarList 
            select new XElement("Bar",
                new XElement("Property1", c.Property1),
                new XElement("Property2", c.Property2)
             ));

Upvotes: 2

Peter Klein
Peter Klein

Reputation: 1010

It has been over 5 years since this item was posted. I give my experience from July 2013 (.NET Framework 4.5). For what it's worth and to whom it may concern:

When I define a class like so: (VB.Net code)

<Serializable> Public Class MyClass
    Public Property Children as List(of ChildCLass)
    <XmlAttribute> Public Property MyFirstProperty as string
    <XmlAttribute> Public Property MySecondProperty as string
End Class

<Serializable> Public Class ChildClass
    <XmlAttribute> Public Property MyFirstProperty as string
    <XmlAttribute> Public Property MySecondProperty as string
End Class

With this definition the class is (de)serialized without any problems. This is the XML that comes out of here:

<MyClass> MyFirstProperty="" MySecondProperty=""
    <Children>
        <ChildClass> MyFirstProperty="" MySecondProperty=""
        </ChildClass>
   </Children>
</MyClass>

It only took me two days to figure it out that the solution was to leave out the <XmlElement> prefix of the List(of T) elements.

Upvotes: 1

FryHard
FryHard

Reputation: 10475

Everything looks great. As @Carl said you need to add the [Serializable] attibute to your classes, but other than that your XML creation should work find.

Foo

[Serializable]
[XmlRoot("Foo")]
public class Foo
{
    [XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName = "Bar")]
    public List<Bar> BarList { get; set; }
}

Bar

[Serializable]
public class Bar
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

Code to test

Foo f = new Foo();
f.BarList = new List<Bar>();
f.BarList.Add(new Bar() { Property1 = "s", Property2 = "2" });
f.BarList.Add(new Bar() { Property1 = "s", Property2 = "2" });

FileStream fs = new FileStream("c:\\test.xml", FileMode.OpenOrCreate);
System.Xml.Serialization.XmlSerializer s = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
s.Serialize(fs, f);

Output

<?xml version="1.0" ?> 
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <BarList>
        <Bar>
            <Property1>s</Property1> 
            <Property2>2</Property2> 
        </Bar>
        <Bar>
            <Property1>s</Property1> 
            <Property2>2</Property2> 
        </Bar>
    </BarList>
</Foo>

Upvotes: 4

Carl
Carl

Reputation: 5990

Just to check, have you marked Bar as [Serializable]?

Also, you need a parameter-less ctor on Bar, to deserialize

Hmm, I used:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {

        Foo f = new Foo();

        f.BarList = new List<Bar>();

        f.BarList.Add(new Bar { Property1 = "abc", Property2 = "def" });

        XmlSerializer ser = new XmlSerializer(typeof(Foo));

        using (FileStream fs = new FileStream(@"c:\sertest.xml", FileMode.Create))
        {
            ser.Serialize(fs, f);
        }
    }
}

public class Foo
{
    [XmlArray("BarList"), XmlArrayItem(typeof(Bar), ElementName = "Bar")]
    public List<Bar> BarList { get; set; }
}

[XmlRoot("Foo")]
public class Bar
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

And that produced:

<?xml version="1.0"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <BarList>
    <Bar>
      <Property1>abc</Property1>
      <Property2>def</Property2>
    </Bar>
  </BarList>
</Foo>

Upvotes: 32

Related Questions