3

I'm trying to preserve data on two different versions of an object and not having any success with it. Can anyone tell me what I'm doing wrong?

Version One of the class:

 [DataContract (Name="Person")]
 public class Person_V1
 {
     [DataMember(Name = "Name")]
     public string Name;

     [DataMember(Name = "Age")]
     public int Age;

     [XmlAnyElement]
     public XmlElement[] XElements;
}

Version Two of the class:

[DataContract(Name = "Person")]
public class Person_V2
{
    [DataMember(Name = "Name")]
    public string Name;

    [DataMember(Name = "Age")]
    public int Age;

    [DataMember(Name = "Weight")]
    public int Weight;

    [XmlAnyElement]
    public XmlElement[] XElements;
}

Saving V2 of object:

private void btnSave_V2_Click(object sender, EventArgs e)
{
    Person_V2 p = new Person_V2() { Name = "Carrie", Age = 31, Weight = 125 };

    var ds = new DataContractSerializer(typeof(Person_V2));

    XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };

    using (XmlWriter w = XmlWriter.Create("person.xml", settings))
        ds.WriteObject(w, p);
}

Restoring V2 of object into V1 object. Would expect to see Weight variable info saved in the XElements array but it doesn't happen

private void btnRestore_V1_Click(object sender, EventArgs e)
{
    Person_V1 p;

    var ds = new DataContractSerializer(typeof(Person_V1));

    XmlReaderSettings settings = new XmlReaderSettings();

    using (XmlReader r = XmlReader.Create("person.xml", settings))
        p = (Person_V1)ds.ReadObject(r);

    MessageBox.Show(p.Name + " " + p.Age);
}
Al Carnali
  • 137
  • 1
  • 7
  • I wouldn't expect to see that at all. – Tony Hopkinson Dec 15 '11 at 12:48
  • Why not? I thought [XmlAnyElement] was a catch-all for any data in the xml file that was not part of the object. Am I totally off here? – Al Carnali Dec 15 '11 at 12:55
  • It's an array though, so they'll be contained by a XElememts Tag. Any other choice would mean if you had Age, Name, Weight inside XElments, it would overwrite, the individual property. Avoiding taht would seriously break the KISS principle. – Tony Hopkinson Dec 15 '11 at 17:09

3 Answers3

2

To preserve unknown elements of future or past versions of DataContracts, you can implement the IExtensibleDataObject interface. Doing so will cause any unknown elements to be placed in a property called ExtensionData which allows future re-serialization without missing data.

Example usage would be:

[DataContract(Name="Person")]
public class Person_V1 : IExtensibleDataObject
{
    [DataMember(Name = "Name")]
    public string Name;

    [DataMember(Name = "Age")]
    public int Age;

    public ExtensionDataObject ExtensionData { get; set; }
}

When the Person_V2 object is deserialized to a Person_V1 object, the Weight property is stored in ExtensionData, and is returned to the serialized stream when it is re-serialized.

Mitch
  • 21,223
  • 6
  • 63
  • 86
2

The Xml*Attribute classes are used by XmlSerializer, not by DataContractSerializer. As far as I know, DataContractSerializer doesn't have any mechanism to handle this scenario. If you can, use XmlSerializer instead, it's much more flexible.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
0

If this is a requirement of your application, you could add a method decorated with the OnDeserializing attribute and then implement your own logic to add missing fields to the XmlElement collection.

competent_tech
  • 44,465
  • 11
  • 90
  • 113