更正缺少的字段值

时间:2012-08-31 17:14:10

标签: c# .net serialization

我有一个类,应该支持版本容忍序列化

[Serializable]
class A {
    [OptionalField]
    int a;

    [OptionalField]
    MyClass b;

    [OptionalField]
    MyClass c;
}
  • 如何在反序列化后更正缺少的字段?我想,我必须使用标有[OnDeserializing]的方法。但是,如何才能忽略哪些字段?
  • 我是否可以将自动反序列化配置为默认构造函数初始化字段,以防它们丢失?

2 个答案:

答案 0 :(得分:3)

此外,您可以使用OnSerializingAttribute和OnSerializedAttribute来设置字段。如示例所示,已设置的字段将保留其值。但请注意,只有在OnSerializing事件期间设置字段时才会出现这种情况。在OnSerialized事件期间设置的字段将覆盖序列化值。

编辑:在这种情况下,如果字段等于null并在必要时进行实例化,则可以检入方法(使用OnSerialized修饰)。如果有可能永远不会使用此字段并且可以推迟创建它,请考虑将有问题的字段隐藏在属性后面并将其实例化为懒惰。

Models.cs:

using System;
using System.Runtime.Serialization;

namespace SerializationExample
{
    [Serializable]
    public class Model
    {
        public Model(){
            A = new SomeClass();
        }

        [OptionalField]
        public int value;

        [OptionalField]
        public SomeClass A;

        [OptionalField]
        public AnotherClass B;            

        [OnDeserializing]
        void OnDeserializing(StreamingContext context)
        {
            B = new AnotherClass("Set during deserializing");
        }

        [OnDeserialized]
        void OnDeserialized(StreamingContext context)
        {
            // Do sth. here after the object has been deserialized
        }

        public override string ToString()
        {
            return String.Format("A: {0}\nB: {1}", A, B);
        }

    }

    [Serializable]
    public class SomeClass
    {
        public string Value { get; set; }

        public SomeClass()
        {
            Value = "Default";
        }

        public override string ToString()
        {
            return Value;
        }
    }

    [Serializable]
    public class AnotherClass
    {
        public string Value { get; private set; }

        public AnotherClass(string v)
        {
            Value = v;
        }

        public override string ToString()
        {
            return Value;
        }
    }
}

的Program.cs:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializationExample
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] FileNames = new string[] {
                @"model1.bin",
                @"model2.bin"
            };

            Stream[] files = new Stream[] {
                File.Create(FileNames[0]),
                File.Create(FileNames[1])
            };

            BinaryFormatter bf = new BinaryFormatter();

            Model m1 = new Model();
            m1.B = new AnotherClass("Set in app");
            m1.A.Value = "Set in app";

            Model m2 = new Model();

            Console.WriteLine("M1:\n{0}\n", m1);
            Console.WriteLine("M2:\n{0}\n\n", m2);

            bf.Serialize(files[0], m1);
            bf.Serialize(files[1], m2);

            foreach (var f in files)
                f.Seek(0, SeekOrigin.Begin);

            m1 = null;
            m2 = null;

            m1 = (Model)bf.Deserialize(files[0]);
            m2 = (Model)bf.Deserialize(files[1]);

            Console.WriteLine("M1:\n{0}\n", m1);
            Console.WriteLine("M2:\n{0}\n\n", m2);

            foreach (var f in files)
                f.Close();           
        }
    }
}

输出:

M1:
A: Set in app
B: Set in app

M2:
A: Default
B:


M1:
A: Set in app
B: Set in app

M2:
A: Default
B: Set during deserializing

答案 1 :(得分:0)

如果您只想将这些值初始化为默认值,那么您只需要初始化它们的默认无参数构造函数。这将在反序列化期间调用,任何缺少的字段将保留在构造函数中初始化它们的任何值。

如果你想要更多的控制,你可以在你的类上实现ISerializable接口和正确的构造函数(你通常应该同时执行这两种操作,但通常只需要一个或另一个。)

如果C#找到带签名的构造函数:

protected A ( SerializationInfo info, StreamingContext context )
{
}

它将调用该构造函数,并传递一个包含所有序列化信息的弱类型字典。 (您可以使用ISerializable::GetObjectData将自定义字段写入对象并在构造函数中检索它们。您可以使用info.GetXXX方法提取这些值。

有一点需要注意:如果您实现此构造函数,则必须执行所有工作,包括通常会自动反序列化的字段。对于任何缺少的字段,只需适当设置它们。同样,如果您实施GetObjectData,则必须在该方法中序列化所有。这很简单,但是如果你改变你的类,你需要适当地编辑自定义方法/构造函数。