Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does XmlSerializer support property name changes (version tolerant)

I have

public bool Included { get; set; }

That I want to change to:

public bool IsIncluded { get; set; }

I want to have backward compatibility. I'd like to only have IsIncluded defined in code but being able to read old xml where the property would be "Included".

Does XmlSerializer support it and how ?

Note: I tried without success... (does not deserialize it)

    public bool IsIncluded { get; set; }

    [Obsolete]
    public bool Included
    {
        set { IsIncluded = value; }
        get { return IsIncluded; }
    }

Update I just want to add that I prefer my solution because I wanted that my xml become IsIncluded which, to me, is more appropriate. Doing as I did (solution below), would enable me to change the xml but keeping previous version working fine. In long term, I would probably be able to remove code to support old version.

Update 2018-02-01 Please take a look at Greg Petersen comment below solution and proposed solution. He propose a great solution in order to encapsulate the correction at the place (the class) where it should be done. WOW!!!

like image 938
Eric Ouellet Avatar asked Sep 27 '13 15:09

Eric Ouellet


People also ask

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization. Note this is the kind of thing I meant. You are not telling the XmlSerializer to ignore namespaces - you are giving it XML that has no namespaces.

Why do we use XmlSerializer class?

Serialization/ De-serialization allow communication with another application by sending and receiving data. With XmlSerializer, you can control how objects are encoded into XML. Call the Serialize method with the parameters of the StreamWriter and object to serialize.

How does the XmlSerializer work C#?

The XmlSerializer creates C# (. cs) files and compiles them into . dll files in the directory named by the TEMP environment variable; serialization occurs with those DLLs. These serialization assemblies can be generated in advance and signed by using the SGen.exe tool.

Is XmlSerializer thread safe?

Since XmlSerializer is one of the few thread safe classes in the framework you really only need a single instance of each serializer even in a multithreaded application.


3 Answers

I found it. This is how I did it.

// ******************************************************************
public static SimulScenario LoadFromFile(string path)
{
    if (!File.Exists(path))
    {
        return new SimulScenarioError(path);
    }

    SimulScenario simulScenario = null;
    XmlTextReader reader = new XmlTextReader(path);
    XmlSerializer x = new XmlSerializer(typeof(SimulScenario));

    x.UnknownAttribute +=x_UnknownAttribute;
    x.UnknownElement += x_UnknownElement;
    x.UnknownNode += x_UnknownNode;
    x.UnreferencedObject += x_UnreferencedObject;

    try
    {
        simulScenario = (SimulScenario)x.Deserialize(reader);
    }
    catch (Exception)
    {
        return new SimulScenarioError(path);
    }
    finally
    {
        reader.Close();
    }

    return simulScenario;
}

static void x_UnreferencedObject(object sender, UnreferencedObjectEventArgs e)
{

}

static void x_UnknownNode(object sender, XmlNodeEventArgs e)
{

}

static void x_UnknownElement(object sender, XmlElementEventArgs e)
{
    var simulChangeState = e.ObjectBeingDeserialized as SimulChangeState;
    if (simulChangeState != null)
    {
        if (e.Element.Name == "Included")
        {
            bool val;
            if (bool.TryParse(e.Element.InnerText, out val))
            {
                simulChangeState.IsIncluded = val;
            }
            else
            {
                throw new FileFormatException(Log.Instance.AddEntry(LogType.LogError, "Invalid scenario file format."));
            }
        }
    }
}

static void x_UnknownAttribute(object sender, XmlAttributeEventArgs e)
{

}
like image 152
Eric Ouellet Avatar answered Sep 28 '22 08:09

Eric Ouellet


Try out:

[XmlElement("Included")]
public bool IsIncluded { get; set; }

Example. Serialization of v1.c and deserialzation of v2.c:

namespace v1
{
    public class c
    {
        public bool Included { get; set; }
    }
}

namespace v2
{
    public class c
    {
        [XmlElement("Included")]
        public bool IsIncluded { get; set; }
    }
}

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            StringWriter sw = new StringWriter();
            new XmlSerializer(typeof(v1.c)).Serialize(sw, new v1.c{ Included=true} );

            StringReader sr = new StringReader( sw.ToString() );
            v2.c cc = (v2.c)new XmlSerializer(typeof(v2.c)).Deserialize(sr);

            Debug.Assert(cc.IsIncluded);
    }
}

}

like image 43
user3967979 Avatar answered Sep 28 '22 09:09

user3967979


I used Eric's solution with a few modifications. I added an interface class to handle the backward compatibility portion.

public interface IBackwardCompatibilitySerializer
{
    void OnUnknownElementFound(string uknownName, string value);
}

Using this, we need to write the unknown element event only once like this

private static void x_UnknownElement(object sender, XmlElementEventArgs e)
{
    var deserializedObj = (e.ObjectBeingDeserialized as IBackwardCompatibilitySerializer);
    if (deserializedObj == null) return;
    deserializedObj.OnUnknownElementFound(e.Element.Name, e.Element.InnerText);
}

Then, for any class where you want to change a variable name, have it implement the interface. Your class would then look like this

public class MyClass : IBackwardCompatibilitySerializer 
{
    // public bool Included { get; set; } Old variable

    public bool IsIncluded { get; set; } // New Variable

    public void OnUnknownElementFound(string unknownElement, string value) 
    {
         switch(unknownElement)
         {
              case "Included":
                    IsIncluded = bool.Parse(value);
                    return;
         }
    }
}

Eric, feel free to incorporate this into your solution if you like.

like image 21
Greg Petersen Avatar answered Sep 28 '22 08:09

Greg Petersen