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!!!
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.
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.
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.
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.
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)
{
}
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);
}
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With