2008-09-17 15 views
87

Estoy buscando una manera fácil de verificar si un objeto en C# es serializable.Cómo comprobar si un objeto es serializable en C#

Como sabemos a tomar un objeto serializable ya sea por aplicación de la interfaz deISerializable o colocando el [Serializable] en la parte superior de la clase.

Lo que estoy buscando es una manera rápida de comprobar esto sin tener que reflejar la clase para obtener sus atributos. La interfaz sería rápida usando una declaración es.

Usando la sugerencia de @ Flard, este es el código que he ideado, gritar es que hay una mejor manera.

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

O mejor aún acaba de obtener el tipo de objeto y luego utilizar la propiedad IsSerializable del tipo:

typeof(T).IsSerializable 

recordar, sin embargo esto parece que esto sólo acaba la clase que nos ocupa, si la clase contiene otras clases. Probablemente quiera verificarlas todas o intentar serializar y esperar errores como lo señaló @pb.

+1

sentimos que falla cuando un campo en obj no es serializable, ver mi muestra . –

+0

Creo que este es un enfoque mucho mejor: http://stackoverflow.com/questions/236599/how-to-unit-test-if-my-object-is-really-serializable/236698#236698 – xero

+0

La declaración "usted hacer un objeto serializable implementando la interfaz ISerializable o colocando el [Serializable] en la parte superior de la clase "es falso. Para que un objeto sea serializable, su clase debe declarar SerializableAttribute. La implementación de ISerializable solo le brinda más control sobre el proceso. – Mishax

Respuesta

99

Usted tiene una hermosa propiedad en la clase Type llamada IsSerializable.

+6

Esto solo le informará si un atributo de Serializable está conectado a su clase. – Fatema

+2

@Fatema: ¿Cuál es su punto? – leppie

+30

Su punto es que los miembros de ese objeto pueden no ser serializables aunque el tipo contenedor sea. ¿derecho? ¿no es el caso que debemos perforar de forma recursiva a los miembros de los objetos y verificar cada uno de ellos, si no solo tratamos de serializarlo y ver si falla? –

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

Probablemente implica la reflexión bajo el agua, pero la manera más simple?

+0

OOOH! Impresionante ... – FryHard

40

Deberá verificar todos los tipos en el gráfico de los objetos que se serializan para el atributo serializable. La forma más fácil es intentar serializar el objeto y capturar la excepción. (Pero esa no es la solución más limpia). Type.IsSerializable y comprobar el atributo serializalbe no tienen en cuenta el gráfico.

Muestra

[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); 
    } 
} 
5

Aquí hay una variación de 3.5 que lo hace disponible para todas las clases usando un método de extensión.

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

Use Type.IsSerializable como otros han señalado.

Probablemente no valga la pena intentar reflejar y verificar si todos los miembros en el gráfico de objeto son serializables.

Un miembro podría ser declarado como un tipo serializable, pero en realidad se crea una instancia como un tipo derivado que no es serializable, como en el siguiente ejemplo artificioso:

[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 

Por lo tanto, incluso si se determina que una la instancia específica de su tipo es serializable, en general no puede asegurarse de que esto sea cierto para todas las instancias.

16

Esta es una vieja pregunta, que puede necesitar ser actualizada para .NET 3.5+. Type.IsSerializable realmente puede devolver false si la clase usa el atributo DataContract.Aquí hay un fragmento que yo uso, si huele mal, que me haga saber :)

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

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

} 
+0

¡Pregunta anterior y respuestas antiguas, pero esto es MUY cierto! Type.IsSerializable es solo una solución parcialmente funcional. De hecho, dado cuántos usan WCF y DataContracts en estos días, ¡en realidad es una solución muy pobre! – Jaxidian

0

El objeto excepción podría ser serializable, pero utilizando otro excepción que no lo es. Esto es lo que acabo de tener con WCF System.ServiceModel.FaultException: FaultException es serializable pero ¡ExceptionDetail no lo es!

Así que estoy usando el siguiente:

// 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); 
    } 
1

Tomé la respuesta en esta pregunta y la respuesta here y lo modificó para que pueda obtener una lista de tipos que no son serializable. De esta forma, puede saber fácilmente cuáles marcar.

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

Y luego lo llamas ...

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

Cuando se ejecuta, nonSerializableTypes tendrán la lista. Puede haber una forma mejor de hacer esto que pasar una Lista vacía al método recursivo. Alguien me corrige si es así.

0

Mi solución, en VB.NET:

para los objetos:

''' <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 

Por Tipos:

''' <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 
Cuestiones relacionadas