如何检查对象是否可在C#中序列化


94

我正在寻找一种简单的方法来检查C#中的对象是否可序列化。

众所周知,您可以通过实现ISerializable接口或将[Serializable]放在类的顶部来使对象可序列化。

我正在寻找的是一种无需查看类即可获取其属性的快速检查方法。使用is语句,该接口将很快。

使用@Flard的建议,这是我想出的代码,尖叫是有更好的方法的。

private static bool IsSerializable(T obj)
{
    return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}

甚至更好的方法是获取对象的类型,然后在该类型上使用IsSerializable属性:

typeof(T).IsSerializable

请记住,尽管这似乎只是我们要处理的类,如果该类包含其他类,您可能要检查所有这些类,或者尝试进行序列化并等待@pb指出的错误。


1
抱歉,当obj中的字段不可序列化时失败,请参见我的示例。
Paul van Brenk

我认为这是一个更好的方法:stackoverflow.com/questions/236599/...
Xero的

语句“通过实现ISerializable接口或通过将[Serializable]放置在类的顶部来使对象可序列化”是错误的。为了使对象可序列化,其类必须声明SerializableAttribute。实施ISerializable仅能使您更好地控制该过程。
Mishax

Answers:


115

您在Type名为的课程上拥有一个不错的选择IsSerializable


7
这只会通知您是否将Serializable属性附加到您的类。
Fatema 2010年

37
他的观点是,即使包含类型是对象,该对象的成员也可能无法序列化。对?不是不是必须递归地钻入那些对象成员并检查每个成员,如果不是只是尝试对其进行序列化并查看它是否失败?
Brian Sweeney

3
例如,对于List <SomeDTO>,即使SomeDTO无法序列化,IsSerializable也为true
Simon Dowdeswell

43

您将必须检查序列化对象图中所有类型的serializable属性。最简单的方法是尝试序列化对象并捕获异常。(但这不是最干净的解决方案)。Type.IsSerializable并检查serializalbe属性不考虑该图。

样品

[Serializable]
public class A
{
    public B B = new B();
}

public class B
{
   public string a = "b";
}

[Serializable]
public class C
{
    public D D = new D();
}

[Serializable]
public class D
{
    public string d = "D";
}


class Program
{
    static void Main(string[] args)
    {

        var a = typeof(A);

        var aa = new A();

        Console.WriteLine("A: {0}", a.IsSerializable);  // true (WRONG!)

        var c = typeof(C);

        Console.WriteLine("C: {0}", c.IsSerializable); //true

        var form = new BinaryFormatter();
        // throws
        form.Serialize(new MemoryStream(), aa);
    }
}

如果成本不是很高,我认为这种方法是最好的。它可以检查不同的序列化要求(二进制,xml)。同样,对象可能具有可以与继承的类类型交换的通用成员,这可能会中断序列化并可能在运行时更改。List(of baseclass)可以添加不能序列化的subclassA项,其中baseclass和subclassB是可以序列化的。
VoteCoffee

此答案使用克隆来检查序列化是否可以往返。它可能在某些情况下是矫枉过正,虽然进行了系列化预计不会设置一些成员:stackoverflow.com/questions/236599/...
VoteCoffee

18

这是一个老问题,可能需要针对.NET 3.5+进行更新。如果类使用DataContract属性,则Type.IsSerializable实际上可以返回false。这是我使用的摘要,如果发臭,请告诉我:)

public static bool IsSerializable(this object obj)
{
    Type t = obj.GetType();

     return  Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)

}

1
老问题和老答案,但这是真的!Type.IsSerializable只是部分功能的解决方案。实际上,考虑到如今有多少人使用WCF和DataContracts,这实际上是一个非常糟糕的解决方案!
Jaxidian 2012年

如果obj以null出现怎么办?
N73k

@ N73k做null检查并返回是否true
FredM

9

正如其他人指出的那样,使用Type.IsSerializable。

可能不值得尝试反映和检查对象图中的所有成员是否可序列化。

可以将成员声明为可序列化的类型,但实际上将其实例化为不可序列化的派生类型,如下面的示例所示:

[Serializable]
public class MyClass
{
   public Exception TheException; // serializable
}

public class MyNonSerializableException : Exception
{
...
}

...
MyClass myClass = new MyClass();
myClass.TheException = new MyNonSerializableException();
// myClass now has a non-serializable member

因此,即使您确定类型的特定实例是可序列化的,也通常不能确保所有实例都适用。


6
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));

可能涉及水下反射,但是最简单的方法是?


5

这是一个3.5版本,可使用扩展方法将其提供给所有类。

public static bool IsSerializable(this object obj)
{
    if (obj is ISerializable)
        return true;
    return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}

2

我将这个问题的答案和此处的答案进行修改,以便您获得无法序列化的类型的列表。这样,您可以轻松知道要标记的内容。

    private static void NonSerializableTypesOfParentType(Type type, List<string> nonSerializableTypes)
    {
        // base case
        if (type.IsValueType || type == typeof(string)) return;

        if (!IsSerializable(type))
            nonSerializableTypes.Add(type.Name);

        foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
        {
            if (propertyInfo.PropertyType.IsGenericType)
            {
                foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments())
                {
                    if (genericArgument == type) continue; // base case for circularly referenced properties
                    NonSerializableTypesOfParentType(genericArgument, nonSerializableTypes);
                }
            }
            else if (propertyInfo.GetType() != type) // base case for circularly referenced properties
                NonSerializableTypesOfParentType(propertyInfo.PropertyType, nonSerializableTypes);
        }
    }

    private static bool IsSerializable(Type type)
    {
        return (Attribute.IsDefined(type, typeof(SerializableAttribute)));
        //return ((type is ISerializable) || (Attribute.IsDefined(type, typeof(SerializableAttribute))));
    }

然后你称之为...

    List<string> nonSerializableTypes = new List<string>();
    NonSerializableTypesOfParentType(aType, nonSerializableTypes);

运行时,nonSerializableTypes将具有列表。这样做可能比将空List传递给递归方法更好。如果是的话,有人纠正我。


0

异常对象可能是可序列化的,但使用了另一个不是。这就是我对WCF System.ServiceModel.FaultException的了解:FaultException是可序列化的,但ExceptionDetail不是!

所以我正在使用以下内容:

// Check if the exception is serializable and also the specific ones if generic
var exceptionType = ex.GetType();
var allSerializable = exceptionType.IsSerializable;
if (exceptionType.IsGenericType)
    {
        Type[] typeArguments = exceptionType.GetGenericArguments();
        allSerializable = typeArguments.Aggregate(allSerializable, (current, tParam) => current & tParam.IsSerializable);
    }
 if (!allSerializable)
    {
        // Create a new Exception for not serializable exceptions!
        ex = new Exception(ex.Message);
    }

0

我的解决方案,在VB.NET中:

对于对象:

''' <summary>
''' Determines whether an object can be serialized.
''' </summary>
''' <param name="Object">The object.</param>
''' <returns><c>true</c> if object can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsObjectSerializable(ByVal [Object] As Object,
                                      Optional ByVal SerializationFormat As SerializationFormat =
                                                                            SerializationFormat.Xml) As Boolean

    Dim Serializer As Object

    Using fs As New IO.MemoryStream

        Select Case SerializationFormat

            Case Data.SerializationFormat.Binary
                Serializer = New Runtime.Serialization.Formatters.Binary.BinaryFormatter()

            Case Data.SerializationFormat.Xml
                Serializer = New Xml.Serialization.XmlSerializer([Object].GetType)

            Case Else
                Throw New ArgumentException("Invalid SerializationFormat", SerializationFormat)

        End Select

        Try
            Serializer.Serialize(fs, [Object])
            Return True

        Catch ex As InvalidOperationException
            Return False

        End Try

    End Using ' fs As New MemoryStream

End Function

对于类型:

''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)() As Boolean

    Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))

End Function

''' <summary>
''' Determines whether a Type can be serialized.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="Type">The Type.</param>
''' <returns><c>true</c> if Type can be serialized; otherwise, <c>false</c>.</returns>
Private Function IsTypeSerializable(Of T)(ByVal Type As T) As Boolean

    Return Attribute.IsDefined(GetType(T), GetType(SerializableAttribute))

End Function
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.