views:

42

answers:

4

Given a type such as:

public class FooList : List<Foo>
{
    public string SomeMessage { get; set; }
}

How can I get the SomeMessage property to be serialized along with the collection without overriding serialization itself?

What I am getting is:

<FooList>
    <Foo />
    <Foo />
    <Foo />
</FooList>

and what I want is:

<FooList>
    <SomeMessage />
    <Foo />
    <Foo />
    <Foo />
</FooList>

For whatever reason, (I think it is because the serialization being used for the generic list doesn't see the new property) it isn't being written out.

In case it matters, here is what I am using to serialize it.

FooList content = new FooList();
content.SomeMessage="this is a test";
//add foos

using (TextWriter writer = new StreamWriter("C:\\foos.xml"))
{
    XmlSerializer serializer = new XmlSerializer(typeof(FooList));
    serializer.Serialize(writer, content);
}
A: 

Try decorating SomeMessage with an [XMLElement] attribute, to force the XMLSerializer to include it.

KeithS
I tried that. I also tried [XmlAttribute] hoping that would do something different...no such luck :(
Joe
+1  A: 

When the XmlSerializer serializes a collection, it only takes the items of the collection into account. Any extra property declared in the class is ignored. You will either have to implement IXmlSerializable to provide your own serialization logic, or change the design of your class.

You could do something like that:

[XmlRoot("FooList")]
public class FooListContainer
{
    public string SomeMessage { get; set; }

    [XmlElement("Foo")]
    public List<Foo> Foos { get; set; }
}
Thomas Levesque
that is what I was afraid of...
Joe
+1  A: 

When deriving from a collection class the XmlSeralizier will only serialize the elements in the collection. One way to work around this is to create a class that wraps the collection and SomeMessage and have that serialized.

  [XmlRoot("FooList")]
  public class CollectionWrapper
  {
     [XmlElement]
     public List<Foo> Items { get; set; }
     public string SomeMessage { get; set; }
  }

Then you could to this:

     CollectionWrapper cw = new CollectionWrapper();
     cw.Items = new List<Foo>();
     cw.Items.Add(foo1);
     cw.Items.Add(foo2);
     cw.SomeMessage = "this is a test";

     using (TextWriter writer = new StreamWriter("C:\\foos.xml"))
     {
        XmlSerializer serializer = new XmlSerializer(typeof(CollectionWrapper));
        serializer.Serialize(writer, cw);
     }

A detailed discussion is available here.

SwDevMan81
A: 

If you need to implement the XmlChoiceIdentifierAttribute, the XmlSerializer will be able to handle your requirements properly.

More info on the topic here:

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlchoiceidentifierattribute.aspx

Sample code:

[System.Xml.Serialization.XmlElementAttribute("myType1", typeof(string))]
[System.Xml.Serialization.XmlElementAttribute("myType2", typeof(string))]
[System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
public string[] Items 
{
    get 
    {
        return this.itemsField;
    }
    set 
    {
        this.itemsField = value;
    }
}

If you prefer to generate the serializable class via xsd.exe, then the xsd schema should look something like this (notice the xs:choice tag):

<xs:complexType name="rowElement">
  <xs:sequence>
    <xs:choice maxOccurs="unbounded">
      <xs:element name="myType1" type="xs:string"/>
      <xs:element name="myType2" type="xs:string"/> 
    </xs:choice>
  </xs:sequence>
</xs:complexType>

HTH...

code4life