62

I'm refactoring some objects that are serialized to XML but need to keep a few properties for backwards compatibility, I've got a method that converts the old object into the new one for me and nulls the obsolete property. I want to use the Obsolete attribute to tell other developers not to use this property but it is causing the property to be ignored by the XmlSerializer.

Similar Code:

[Serializable]
public class MySerializableObject
{
    private MyObject _oldObject;
    private MyObject _anotherOldObject;

    private MyObject _newBetterObject;

    [Obsolete("Use new properties in NewBetterObject to prevent duplication")]
    public MyObject OldObject
    {
      get { return _oldObject; }
      set { _oldObject = value; }
    }

    [Obsolete("Use new properties in NewBetterObject to prevent duplication")]
    public MyObject AnotherOldObject
    {
      get { return _anotherOldObject; }
      set { _anotherOldObject = value; }
    }

    public MyObject NewBetterObject
    {
      get { return _anotherOldObject; }
      set { _anotherOldObject = value; }
    } 
}

Any ideas on a workaround? My best solution is to write obsolete in the XML comments...

Update: I'm using .NET 2.0

jv42
  • 8,521
  • 5
  • 40
  • 64
Rob Stevenson-Leggett
  • 35,279
  • 21
  • 87
  • 141

7 Answers7

45

EDIT: After reading a MS Connect article, it appears that .Net 2.0 has a 'feature' where it makes ObsoleteAttribute equivalent to XmlIgnoreAttribute without any notification in the documentation. So I'm going to revise my answer to say that the only way to have your cake and eat it too in this instance is to follow @Will's advice and implement serialization manually. This will be your only future proof way of including Obsolete properties in your XML. It is not pretty in .Net 2.0, but .Net 3.0+ can make life easier.

From XmlSerializer:

Objects marked with the Obsolete Attribute no longer serialized In the .NET Framework 3.5 the XmlSerializer class no longer serializes objects that are marked as [Obsolete].

user7116
  • 63,008
  • 17
  • 141
  • 172
11

Another workaround is to subscribe to XmlSerializer.UnknownElement, when creating the serializer for the datatype, and then fix old data that way.

http://weblogs.asp.net/psteele/archive/2011/01/31/xml-serialization-and-the-obsolete-attribute.aspx

Maybe consider to have the method for subscribing as a static method on the class for datatype.

static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
    if( e.Element.Name != "Hobbies")
    {
        return;
    }

    var target = (MyData) e.ObjectBeingDeserialized;
    foreach(XmlElement hobby in e.Element.ChildNodes)
    {
        target.Hobbies.Add(hobby.InnerText);
        target.HobbyData.Add(new Hobby{Name = hobby.InnerText});
    }
}
Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70
4

I have struggled with this a lot - there is no solution other than doing serialization manually or using another serializer.

However, instead of writing shims for each obsolete property which quickly becomes a pain, you could consider adding an Obsolete prefix to property names (e.g. Foo becomes ObsoleteFoo. This will not generate a compiler warning like the attribute will, but at least it's visible in code.

georgiosd
  • 3,038
  • 3
  • 39
  • 51
  • 1
    Unless all of your code is in one solution, changing the name of the obsolete library function will cause your other program to crash. If all of your code is in one solution, just delete the obsolete property and fix all the references to use the replacement. – Denise Skidmore Jun 19 '19 at 23:05
2

1) WAG: Try adding the XmlAttributeAttribute to the property; perhaps this will override the ObsoleteAttribute
2) PITA: Implement IXmlSerializable

  • 1
    @Carson63000: But #2 does. Its just a pain in the ass. If you're looking at doing serialization now, I'd suggest avoiding the xml serializer and go with the DataContractSerializer, NetDataContractSerializer or even xaml serialization, depending on your needs/abilities. And there are other options, too, like serializing to json or protobuf (our own Marc Gravell runs protobuf.net, btw). Lots more options nowadays than back in '08 –  Aug 22 '12 at 12:44
  • 5
    What does WAG mean? Is it a "wild ass guess"? Your use of acronyms makes the answer confusing to read. – ANeves Feb 13 '18 at 20:37
2

Yes I agree with marking things with the name "Obsolete" we do this with Enum values

/// <summary>
/// Determines the swap file location for a cluster.
/// </summary>
/// <remarks>This enum contains the original text based values for backwards compatibility with versions previous to "8.1".</remarks>
public enum VMwareClusterSwapFileLocation
{

    /// <summary>
    /// The swap file location is unknown.
    /// </summary>
    Unknown = 0,

    /// <summary>
    /// The swap file is stored in the virtual machine directory.
    /// </summary>
    VmDirectory = 1,

    /// <summary>
    /// The swap file is stored in the datastore specified by the host.
    /// </summary>
    HostLocal = 2,

    /// <summary>
    /// The swap file is stored in the virtual machine directory. This value is obsolete and used for backwards compatibility.
    /// </summary>
    [XmlElement("vmDirectory")]
    ObseleteVmDirectory = 3,

    /// <summary>
    /// The swap file is stored in the datastore specified by the host. This value is obsolete and used for backwards compatibility.
    /// </summary>
    [XmlElement("hostLocal")]
    ObseleteHostLocal = 4,




}
M.T
  • 4,917
  • 4
  • 33
  • 52
David Homer
  • 396
  • 2
  • 8
1

I recently came across the exact same scenario as you're describing. Doing some digging, I found out that it's possible to override how a given property is serialized with XmlAttributeOverrides, and with this you can tell the serializer to not ignore a given property.

var overrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes { XmlIgnore = false };
overrides.Add(typeof(MyClass), nameof(MyClass.MyObsoleteProperty), attributes);

var serializer = new XmlSerializer(typeof(MyClass), overrides);

Here's a fiddle demonstrating this approach.

This could potentially even be extended to use reflection to find and 'un-ignore' all Obsolete properties if needed.

Justin
  • 49
  • 1
  • 1
  • 6
0

You may try the following workaround:

add a method named

ShouldSerializeOldObject ()
{
   return true;
}

ShouldSerializeAnotherOldObject ()
{
   return true
}

this may override the obsolete Attribute

Bluenuance
  • 4,813
  • 4
  • 23
  • 19
  • by adding a method "ShouldSerialize*" you tell the XmlSerializer if you want the Property to be serialized (normally used in cases where - i don't know - you just want to serialize an int value if it's larger than 5 it MAY work here too – Bluenuance Dec 01 '08 at 14:57
  • 2
    It didn't work for me... the ShouldSerialize method was only invoked for non obsolete properties. Also note that your properties should be also decorated with DefaultValue(null) for the corresponding ShouldSerialize method to be invoked. – JCallico Jul 11 '11 at 21:00
  • 1
    Did this method actually work for someone? It is marked as answer, but I can't find documentation to support this use (http://msdn.microsoft.com/en-us/library/53b8022e.aspx) and it doesn't work for me. – jv42 Jan 12 '12 at 17:23
  • it does (or did?) work. See here: http://msdn.microsoft.com/en-us/library/53b8022e(v=vs.85).aspx – Bluenuance Nov 07 '13 at 13:38
  • @Bluenuance It does not work for members marked as Obsolete. For these members the ShouldSerializeX method is not called. – Onots Apr 25 '16 at 07:06