Does XmlSerializer support property name changes (version tolerant)

会有一股神秘感。 提交于 2021-01-27 04:56:10

问题


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!!!


回答1:


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)
{

}



回答2:


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);
    }
}

}




回答3:


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.



来源:https://stackoverflow.com/questions/19054730/does-xmlserializer-support-property-name-changes-version-tolerant

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!