使用XML序列化加载对象的多个版本

时间:2015-01-07 13:25:52

标签: c# xml serialization

我已经获得了一个项目,我将在其中设计一个普遍的"存储商店值并根据需要修改它们的方法(在运行程序之外)。这些值用于测试目的(即最大和最小电压和电流阈值)。理想情况下,我应该能够在多个程序和项目中共享这些值。

过去,我们会为此目的定义自己的脚本文件(通常是某种形式的CSV文件),不用说,它是凌乱和低效的。

现在,我给出这个项目时的直接想法是使用对象序列化(特别是XML序列化,因为它是人类可读的)。我们的测试是多种多样的,以至于没有办法提出一个普遍的"格式;所以接下来要做的最好的事情是自动化格式化。它允许我在程序输入时反序列化对象,然后在我完成它时再次序列化它。最重要的是,我可以进入生成的XML文件并手动修改其内容。另外,我可以像常规对象一样使用数据。所以我开始将所有这些值存储在对象中并将它们序列化。

但是,我们的测试参数经常变化;有时我们可能会添加或删除测试用例等等。不幸的是,为了做到这一点,我需要修改我的对象。现在我离开了同一个对象的不同版本,我需要支持所有这些版本(或者至少将数据上转换为最新版本的对象类型)。

当然,一种解决方案是为每个版本使用继承,但这会变得混乱,并且它不是一种自然的编码方式(特别是在您测试硬件时)。反思可能是另一种选择,但我认为它涉及一个手动解析器(我仍然需要支持多个版本)。我很欣赏有关最佳方法的任何意见。谢谢

请注意,我已使用DataContractSerializer来处理序列化和反序列化。


这是我的代码如何工作的示例类; :

主要功能:

namespace Test
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            SerializableSingleton<Class1>.initInstance("class1Singleton.xml");
            Class1 obj1 = SerializableSingleton<Class1>.Instance;

            Class1 obj2 = new SerializableInstance<Class1>("class1Instance.xml").Data;
        }
    }
}

要序列化的类:

namespace Test
{
    [DataContract()]
    class Class1 : AbstractSerializeableObject
    {
        protected override XMLVersion ObjectVersion
        {
            get { return version; }
        }

        XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");


        [DataMember(Name = "First Number")]
        int number = 1;

        [DataMember()]
        string str = "hello";

        [DataMember(Name = "Nested Class")]
        NestedClass1 nClass = new NestedClass1();

        [DataContract()]
        class NestedClass1 :AbstractSerializeableObject
        {
            protected override XMLVersion ObjectVersion
            {
                get { return version; }
            }

            XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");

            [DataMember(Name = "Second Number")]
            int number = 2;

            [DataMember()]
            string str = "world";
        }

    }
}

生成的XML文件:

class1Singleton.xml

<?xml version="1.0" encoding="utf-8"?>
<Class1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Test">
    <First_x0020_Number>1</First_x0020_Number>
    <Nested_x0020_Class>
        <Second_x0020_Number>2</Second_x0020_Number>
        <str>world</str>
    </Nested_x0020_Class>
    <str>hello</str>
</Class1>

class1Instance.xml

<?xml version="1.0" encoding="utf-8"?>
<Class1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Test">
    <First_x0020_Number>1</First_x0020_Number>
    <Nested_x0020_Class>
        <Second_x0020_Number>2</Second_x0020_Number>
        <str>world</str>
    </Nested_x0020_Class>
    <str>hello</str>
</Class1>

请注意,此处的所有内容均应显示;生成的XML文件没有问题(到目前为止)。这些是我期望的结果。单例和实例的内容有很多层,并不是什么重要的事情。重要的是序列化本身。重要的部分是序列化本身和版本控制。

以下是我如何进行序列化和反序列化:

namespace XMLSerializationLib
{
    /// <summary>
    /// The XmlSerializer is used solely to encapulate the serialization and
    /// deserialization of objects. It uses generics so most objects can make use
    /// of this library, provided that they heed to the warning(s) below:
    ///
    /// -- The object to be serialized MUST have a default, non-parameterized
    ///    contructor
    ///
    /// </summary>
    /// <typeparam name="T"> The object type to be serialized/deserialized </typeparam>
    internal static class XmlSerializer<T>
    {
        /* ========================================================================= */
        /* Functions                                                                 */
        /* ========================================================================= */

        /// <summary>
        /// Serializes an object of type T into an XML file
        /// which can be edited outside of the program.
        /// </summary>
        /// <param name="obj"> The object which should be serialized </param>
        /// <param name="path"> The path of the XML file to be generated </param>
        internal static void Serialize(T obj, string path)
        {
            if (!(obj is AbstractSerializeableObject))
                throw new SerializerTypeException("Serialized Object must inherit AbstractSerializeableObject");

            // Make XML readable
            var settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "\t";


#if _NET_4_5_
            // Enable serialization of `readonly` properties and types
            System.Runtime.Serialization.SerializeReadOnlyTypes = true;
#endif

            // Write to XML
            using (var writer = XmlWriter.Create(path, settings))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                serializer.WriteObject(writer, obj);
                writer.Close();
            } 
        }

        /// <summary>
        /// Deserializes a provided XML file and stores the retrieved data inside
        /// an object of type T.
        /// </summary>
        /// <param name="path"> The path of the XML file to be loaded </param>
        /// <returns>
        /// An filled object of type T; if the provided XML cannot be
        /// deserialized, then it returns a T object with its default
        /// properties.
        /// </returns>
        internal static T Deserialize(string path)
        {
            T obj;

            DataContractSerializer deserializer = new DataContractSerializer(typeof(T));
            XmlReader reader = new XmlTextReader(path);

#if _NET_4_5_
            // Enable deserialization of `readonly` properties and types
            System.Runtime.Serialization.SerializeReadOnlyTypes = true;
#endif

            if (deserializer.IsStartObject(reader))
                obj = (T)deserializer.ReadObject(reader);
            else
                obj = Activator.CreateInstance<T>();

            reader.Close();

            if (!(obj is AbstractSerializeableObject))
                throw new SerializerTypeException("Serialized Object must inherit AbstractSerializeableObject");

            return obj;
        }

        /// <summary>
        /// Exception to be thrown if the provided object T does not inherit
        /// AbstractSerializeableObject.
        /// </summary>
        internal class SerializerTypeException : Exception
        {
            /// <summary>
            /// Exception constructor
            /// </summary>
            /// <param name="message"> Error message </param>
            public SerializerTypeException(string message)
                : base(message)
            {
            }
        }


 }
}

现在问题如下:假设我要使用Class1对象并将number字段修改为等于382000。然后,我将完全删除str字段,并添加一个名为dbl的新字段,其值为pi。结果类看起来像这样:

修改后的Class1

namespace Test
{
    [DataContract()]
    class Class1 : AbstractSerializeableObject
    {
        protected override XMLVersion ObjectVersion
        {
            get { return version; }
        }

        XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");


        [DataMember(Name = "First Number")]
        int number = 382000;

        [DataMember()]
        double dbl = 3.14;

        [DataMember(Name = "Nested Class")]
        NestedClass1 nClass = new NestedClass1();

        [DataContract()]
        class NestedClass1 :AbstractSerializeableObject
        {
            protected override XMLVersion ObjectVersion
            {
                get { return version; }
            }

            XMLVersion version = new XMLVersion(1, 0, 0, "Init Version");

            [DataMember(Name = "Second Number")]
            int number = 2;

            [DataMember()]
            string str = "world";
        }

    }
}

现在假设我在一台计算机(计算机1 )上使用原始版本的Class1运行我的程序的旧版本,并使用新的{{1>更新版本在另一台机器上(机器3 )。为了论证,让我们说我在第三台机器上运行了Class1的中间版本( Machine2 )。现在我希望能够获取 Machine 1 生成的XML文件并快进,以便兼容数据能够在 Machine 2 和< strong>机器3 ;所有新的数据字段当然都会占用一些默认值。

1 个答案:

答案 0 :(得分:0)

在类似的情况下,我使用控制台应用程序将旧的xml更新为新结构。 为此,我写了我对IDataContractSurrogate的认识:

public class SerializerTypeSurrogate : IDataContractSurrogate
        {
            public virtual Type GetDataContractType(Type type)
            {
                return type == typeof(Charge) ? typeof(OldCharge) : type;
            }

            public object GetObjectToSerialize(object obj, Type targetType)
            {
                return obj;
            }

            public virtual object GetDeserializedObject(Object obj, Type targetType)
            {
                var oldClass1= obj as OldClass1;
                        return oldClass1 != null ? new Class1(oldClass1) : obj;
        }

            public Type GetReferencedTypeOnImport(string typeName,
                string typeNamespace, object customData)
            {
                return null;
            }

            public System.CodeDom.CodeTypeDeclaration ProcessImportedType(
                System.CodeDom.CodeTypeDeclaration typeDeclaration,
                System.CodeDom.CodeCompileUnit compileUnit)
            {
                return typeDeclaration;
            }



            public object GetCustomDataToExport(Type clrType, Type dataContractType)
            {
                return null;
            }

            public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
            {
                return null;
            }

            public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
            {
            }
        }

你应该暂时保留旧的和新的类,新类应该有来自旧类对象的构造函数,但是你可以在以新形式转换所有旧的xmls之后删除它。

要从旧表单转换为新表单,您应该使用下一个代码:

    var oldentity = Deserialize<T>(oldXml,
        new SerializerTypeSurrogate());
    var newXml = FormalizedChangeRecord.Serialize<T>(card);

public static TCard Deserialize<T>(string xmlData, IDataContractSurrogate surrogate)            {
            var serializer = new DataContractSerializer(typeof (T), new Type[0], 10000, true, true, surrogate);
        using (var stringReader = new StringReader(changeRecord.CardData))
        {
            using (var xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings { CloseInput = false }))
            {
                var entity= (TCard)serializer.ReadObject(xmlReader);
                return entity;
            }
        }
    }
public static string Serialize<T>(T entity)
    {
        var serializer = new DataContractSerializer(typeof (T));
            using (var stringWriter = new StringWriter())
            {
                using (
                    var xmlWriter = XmlWriter.Create(stringWriter,
                        new XmlWriterSettings
                {
                            CloseOutput = false,
                            Encoding = new UTF8Encoding(false),
                            OmitXmlDeclaration = true,
                            Indent = true
                        }))
                {
                    serializer.WriteObject(xmlWriter, entity);
                }
                return stringWriter.ToString();
            }
        }