Cómo verificar si un objeto es serializable en C #

94

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

Como sabemos, usted crea un objeto serializable implementando la interfaz ISerializable o colocando [Serializable] en la parte superior de la clase.

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

Usando la sugerencia de @ Flard, este es el código que se me ocurrió, grito si hay una manera mejor.

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

O incluso mejor, simplemente obtenga el tipo de objeto y luego use la propiedad IsSerializable en el tipo:

typeof(T).IsSerializable

Sin embargo, recuerde que esto parece ser solo la clase con la que estamos tratando si la clase contiene otras clases, probablemente desee verificarlas todas o intente serializar y esperar errores como señaló @pb.

FryHard
fuente
1
Lo siento, eso falla cuando un campo en obj no es serializable, vea mi muestra.
Paul van Brenk
Creo que este es un enfoque mucho mejor: stackoverflow.com/questions/236599/…
xero
La declaración "usted hace que un objeto sea serializable implementando la interfaz ISerializable o colocando el [Serializable] en la parte superior de la clase" es falsa. 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

Respuestas:

115

Tienes una hermosa propiedad en la Typeclase llamada IsSerializable.

leppie
fuente
7
Esto solo le informará si un atributo de Serializable está adjunto a su clase.
Fatema
37
su punto es que los miembros de ese objeto podrían no ser serializables aunque el tipo contenedor sí lo sea. ¿Derecha? ¿No es el caso que debemos profundizar de forma recursiva en los miembros de los objetos y comprobar cada uno, si no es que simplemente intentamos serializarlo y ver si falla?
Brian Sweeney
3
Por ejemplo, para una List <SomeDTO>, IsSerializable es verdadero incluso si SomeDTO NO es serializable
Simon Dowdeswell
43

Tendrá que verificar todos los tipos en el gráfico de objetos que se serializan para el atributo serializable. La forma más sencilla es intentar serializar el objeto y detectar la excepción. (Pero esa no es la solución más limpia). Type.IsSerializable y la comprobación del 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);
    }
}
Paul van Brenk
fuente
Si el costo no es demasiado alto, creo que este enfoque es el mejor. Puede verificar diferentes requisitos de serialización (binario, xml). Además, un objeto puede tener un miembro genérico que se puede intercambiar con tipos de clases heredados que pueden interrumpir la serialización y pueden cambiar en tiempo de ejecución. List (Of baseclass) podría tener elementos agregados de subclassA que no es serializable, donde baseclass y subclassB son serializables.
VoteCoffee
Esta respuesta usa la clonación para verificar si la serialización puede ser de ida y vuelta. Puede ser excesivo en algunos casos, aunque no se espera que la serialización establezca algunos miembros: stackoverflow.com/questions/236599/…
VoteCoffee
18

Esta es una pregunta antigua, que puede necesitar ser actualizada para .NET 3.5+. Type.IsSerializable puede devolver falso si la clase usa el atributo DataContract. Aquí hay un fragmento que uso, si huele mal, avíseme :)

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

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

}
Mike_G
fuente
1
Pregunta antigua y respuestas antiguas, ¡pero esto es MUY cierto! Type.IsSerializable es solo una solución parcialmente funcional. De hecho, dada la cantidad de usuarios que usan WCF y DataContracts en estos días, ¡en realidad es una solución muy pobre!
Jaxidian
¿Qué pasa si obj entra como nulo?
N73k
@ N73k hacer una nullverificación y devolver si true?
FredM
9

Use Type.IsSerializable como otros han señalado.

Probablemente no valga la pena intentar reflexionar y verificar si todos los miembros del gráfico de objetos son serializables.

Un miembro podría declararse como un tipo serializable, pero de hecho puede instanciarse como un tipo derivado que no es serializable, como en el siguiente ejemplo artificial:

[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 determina que una instancia específica de su tipo es serializable, en general no puede estar seguro de que esto sea cierto para todas las instancias.

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

Probablemente implique la reflexión bajo el agua, pero ¿la forma más sencilla?

Grad van Horck
fuente
5

Aquí hay una variación 3.5 que la pone a disposición de todas las clases mediante 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));
}
Michael Meadows
fuente
2

Tomé la respuesta a esta pregunta y la respuesta aquí y la modifiqué para que obtenga una Lista de tipos que no se pueden serializar. De esa manera, 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 ejecute, nonSerializableTypes tendrá la lista. Puede haber una mejor manera de hacer esto que pasar una Lista vacía al método recursivo. Alguien me corrija si es así.

teja de alcantarilla
fuente
0

El objeto de excepción puede ser serializable, pero usando otra excepción que no lo es. Esto es lo que acabo de tener con WCF System.ServiceModel.FaultException: ¡FaultException es serializable pero ExceptionDetail no!

Entonces estoy usando lo 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);
    }
Eric
fuente
0

Mi solución, en VB.NET:

Para 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

Para 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
ElektroStudios
fuente