views:

410

answers:

3

Hi All,

I've got 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 with the attribute

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

in an attempt to tell the Serialiser 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.

Apologies for the length of the question - any insight on how I might make this work would be much appreciated.

Edit:

Thanks for the quick responses guys - I've gotten that working now!

+2  A: 

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"&gt;
  <BarList>
    <Bar>
      <Property1>abc</Property1>
      <Property2>def</Property2>
    </Bar>
  </BarList>
</Foo>
Carl
+1  A: 

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"&gt;
    <BarList>
     <Bar>
      <Property1>s</Property1> 
      <Property2>2</Property2> 
     </Bar>
     <Bar>
      <Property1>s</Property1> 
      <Property2>2</Property2> 
     </Bar>
    </BarList>
</Foo>
FryHard
A: 

Ah, now that's interesting - I thought that the [Serializable] attribute was just for Binary and SOAP serialization? Please correct me if that's wrong, as I haven't marked either class as such...

Do I need to explicitly define a constructor? I know there's an implicit parameter-less constructor and had hoped that would do the job... That said, I haven't tried to deserialize yet so if it fails miserably, I'll know why - thanks for the heads-up!

Jon Artus
I think that the implicit parameter-less constructor is only created when there are no other constructors.
Carl
Re: the [Serializable], my sample didn't have it and it worked fine, so I'm not really sure.
Carl
Carl is correct... a default constructor is only implicitly added if there are no explicit constructors.
Simon Gillbee