using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; namespace XmlVersioning { // THE TYPE FOR DEVELOPERS public class SampleFile : SampleFileV2 { // LEAVE EMPTY } [XmlInclude(typeof(SampleFile))] public class SampleFileV2 { public string Name { get; set; } public List Warnings { get; set; } } public class SampleFileV1 { public string Name { get; set; } public List Warnings { get; set; } // The translation (Casting) public static implicit operator SampleFile(SampleFileV1 file) { var result = new SampleFile { Name = file.Name, Warnings = new List() }; foreach (var warning in file.Warnings) { result.Warnings.Add(new SampleFileWarning() { Name = warning, Priority = "Default" }); } return result; } } public class SampleFileWarning { public string Priority { get; set; } public string Name { get; set; } } public class SampleFileSerializer { public SampleFile Deserialize(StreamReader reader) { var type = GetXmlType(reader); var serializer = new XmlSerializer(type); var obj = serializer.Deserialize(reader); var caster = type.GetMethod("op_Implicit"); if (caster != null) { return (SampleFile) caster.Invoke(obj, new [] { obj}); } return (SampleFile) obj; } public void Serialize(StreamWriter sw, SampleFile file) { var type = GetCurrentSpecificVersion(); if (type == null) { throw new InvalidOperationException("Unable to find current specific version"); } new XmlSerializer(type).Serialize(sw, file); } private Type GetCurrentSpecificVersion() { foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { if (type.IsAssignableFrom(type) && typeof(SampleFile) != type) { return type; } } return null; } private Type GetXmlType(StreamReader streamReader) { try { using (var reader = XmlReader.Create(streamReader)) { while (reader.Read()) { if (reader.IsStartElement()) { var assembly = Assembly.GetExecutingAssembly(); var lookFor = string.Format("{0}.{1}", assembly.GetName().Name, reader.Name); return assembly.GetType( lookFor); } } } return null; } finally { streamReader.BaseStream.Position = 0; } } } class Program { private static readonly FileInfo[] TestFiles = { new FileInfo("v1.xml"), new FileInfo("v2.xml") }; static void Main(string[] args) { CreateTestFiles(); SerializeAndDeserialize(); } static void CreateTestFiles() { using (var sw = new StreamWriter(TestFiles[0].Create())) { var serializer = new XmlSerializer(typeof(SampleFileV1)); var file = new SampleFileV1() { Name = "Dave", Warnings = new List() { "A", "B", "C" } }; serializer.Serialize(sw, file); } } static void SerializeAndDeserialize() { //Confirm TestFiles[0] is SampleFileV1 var xmlSerializer = new XmlSerializer(typeof(SampleFileV1)); using (var sr = new StreamReader(TestFiles[0].OpenRead())) { var testFile = (SampleFileV1) xmlSerializer.Deserialize(sr); if (testFile.GetType() != typeof(SampleFileV1)) { throw new InvalidOperationException("testFile is not SampleFileV1"); } } // Version 1 as Version 2 var serializer = new SampleFileSerializer(); SampleFile file; // Read a version 1 file using (var sr = new StreamReader(TestFiles[0].OpenRead())) { file = serializer.Deserialize(sr); CheckWarnings(file); ConfirmBaseType(file, typeof(SampleFileV2)); } //Save it as version 2 file using (var sw = new StreamWriter(TestFiles[1].Create())) { serializer.Serialize(sw, file); } // Read a version 2 file using (var sr = new StreamReader(TestFiles[1].OpenRead())) { var versionFile = serializer.Deserialize(sr); CheckWarnings(versionFile); ConfirmBaseType(file, typeof(SampleFileV2)); } } private static void ConfirmBaseType(SampleFile file, Type t) { if (file.GetType().BaseType != t) { throw new InvalidOperationException(string.Format("Expecting {0} got {1}", t, file.GetType().BaseType)); } } private static void CheckWarnings(SampleFile file) { if (file.Warnings.Count != 3) { throw new InvalidOperationException("Warnings should be 3"); } } } }