在二进制序列化(C#)中如何跳过Serializable对象中的非可序列化字段?

时间:2015-11-17 11:31:10

标签: .net exception

我有一个标有[Serializable]属性的类。这个类有很多不同类型的字段。此类引用的大多数类型都具有[Serializable]属性,但其中一些不具备此属性。

二进制序列化在上述场景中失败,这是预期的。

我在上面的描述中简化了问题。实际上,我正在处理一个具有复杂对象层次结构的大型企业应用程序,并且在层次结构深处有一些无法序列化的窗口类型,二进制序列化失败。我想要一个解决方案。

是否有自定义二进制序列化程序可以跳过无法序列化的字段?

注意:我知道这个概念: How do you identify the field that is causing binary serialization to fail in .NET?

我正在寻找更优雅的解决方案。

2 个答案:

答案 0 :(得分:0)

您可以在字段上添加[NonSerialized]属性,以便在您确定无法序列化后将其完全跳过。

如果它在层次结构中的类型更远,你无法控制它导致这个,那么我建议的是复制有问题的类/结构的结构sans-unserializable-fields并使用像automapper这样的东西将值深度复制到新类,然后序列化最终结果。

答案 1 :(得分:0)

我写了一个帮助我解决这个问题的示例程序。

想象一个像这样构造的对象: Sample Object Structure

class Program
{
    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder();
        Root rootObject = new Root();

        FindNonSerializableFields(rootObject, rootObject.GetType().Name, sb);
        string unserializedFields = sb.ToString();
        bool isObjectSerializable = string.IsNullOrEmpty(unserializedFields);
        Console.WriteLine("Is object Serializable? : {0}", isObjectSerializable);
        if (!isObjectSerializable)
        {
            Console.WriteLine("Unserializable Fields:");
            Console.WriteLine(unserializedFields);
        }
    }

    static void FindNonSerializableFields(object obj, string lineage, StringBuilder sb, uint stopAtDepth = 7)
    {
        if (lineage.Count(f => f == '»') >= stopAtDepth)
        {
            return;
        }

        if (!IsObjectBinarySerializable(obj))
        {
            sb.AppendLine(lineage);
            var fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            foreach (var field in fields)
            {
                object fieldObject = field.GetValue(obj);
                if (fieldObject != null && !Attribute.IsDefined(field, typeof(NonSerializedAttribute)))
                {
                    FindNonSerializableFields(fieldObject, lineage + "»" + fieldObject.GetType().Name, sb);
                }
            }
        }
    }

    static bool IsObjectBinarySerializable(object obj)
    {
        try
        {
            using (MemoryStream memorystream = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(memorystream, obj);
            }
        }
        catch(SerializationException)
        {
            return false;
        }

        return true;
    }
}

[Serializable]
class Root
{
    public L1B1 l1b1 = new L1B1();
    private L1B2 l1b2 = new L1B2();
    protected L1B3 l1b3 = new L1B3();
}

[Serializable]
class L1B1
{
    public L2B1 l2b1 = new L2B1();
}

[Serializable]
class L1B2
{
    private L2B2 l2b2 = new L2B2();
}

[Serializable]
class L1B3
{
    protected L2B3 l2b3 = new L2B3();
}

//[Serializable]
class L2B1
{
    protected Leaf_B1 leaf_b1 = new Leaf_B1();
}

[Serializable]
class L2B2
{
    public Leaf_B2 leaf_b2 = new Leaf_B2();
}

[Serializable]
class L2B3
{
    private Leaf_B3 leaf_b3 = new Leaf_B3();
}

[Serializable]
class Leaf_B1
{
    [NonSerialized]
    private int var1 = 10;
    public string var2 = "string_Leaf_B1";
    public bool var3 = true;
}

[Serializable]
class Leaf_B2
{
    private int var1 = 10;
    [NonSerialized]
    public string var2 = "string_Leaf_B2";
    public bool var3 = true;
}

//[Serializable]
class Leaf_B3
{
    private int var1 = 10;
    public string var2 = "string_Leaf_B3";
    [NonSerialized]
    public bool var3 = true;
}

示例输出:

对象是否可以序列化? :错误

不可序列化的字段:

根»L1B1

根»L1B1»L2B1

这样我知道序列化的确切位置。

在编写此示例时,我没有考虑性能或任何编码标准。 很高兴看到有人提出更好的解决方案。