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

本文关键字:版本 对象 XML 序列化 加载 使用 | 更新日期: 2023-09-27 18:36:39

我得到了一个项目,在这个项目中,我将设计一种"通用"的方式来存储存储值并根据需要修改它们(在正在运行的程序之外)。这些值用于测试目的(即.max以及最小电压和电流阈值)。理想情况下,我也应该能够在多个程序和项目中共享这些价值观。

过去,我们会为此目的定义自己的脚本文件(通常是某种形式的 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 文件:

类1单例.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>

类1实例.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 。生成的类将如下所示:

修改后的类 1

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)上运行旧版本的

程序,在一台机器(机器 1)上使用原始版本的 Class1,在另一台机器(机器 3)上使用新Class1运行更新版本。为了论证起见,假设我在第三台机器(Machine2)上运行了一些中间版本的Class1。现在我希望能够获取机器 1 生成的 XML 文件并快进它,以便兼容的数据能够在机器 2 和机器 3 上工作;当然,所有新的数据字段都将采用一些默认值。

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

在类似的情况下,我使用控制台应用程序将旧的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)
            {
            }
        }
您应该暂时保留旧类和新类,新类

应该具有旧类对象的构造函数,但是您可以在以新形式转换所有旧 xml 后将其删除。

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

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