Como verificar se um objeto é serializável em C #

94

Estou procurando uma maneira fácil de verificar se um objeto em C # é serializável.

Como sabemos, você torna um objeto serializável implementando a interface ISerializable ou colocando o [Serializable] no topo da classe.

O que estou procurando é uma maneira rápida de verificar isso sem ter que refletir a classe para obter seus atributos. A interface seria rápida usando uma instrução is .

Usando a sugestão de @ Flard, este é o código que eu criei, grite se existe uma maneira melhor.

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

Ou melhor ainda, obter o tipo do objeto e usar a propriedade IsSerializable no tipo:

typeof(T).IsSerializable

Lembre-se de que isso parece apenas para a classe com a qual estamos lidando, se a classe contiver outras classes, você provavelmente deseja verificar todas ou tentar serializar e aguardar os erros como @pb apontado.

FryHard
fonte
1
Desculpe, isso falha quando um campo em obj não é serializável, veja meu exemplo.
Paul van Brenk,
Acho que esta é uma abordagem muito melhor: stackoverflow.com/questions/236599/…
xero
A declaração "você torna um objeto serializável implementando a interface ISerializable ou colocando o [Serializable] no topo da classe" é falsa. Para um objeto ser serializável, sua classe deve declarar o SerializableAttribute. Implementar ISerializable apenas dá a você mais controle sobre o processo.
Mishax

Respostas:

115

Você tem uma linda propriedade na Typeclasse chamada IsSerializable.

leppie
fonte
7
Isso apenas informará se um atributo de Serializable está anexado à sua classe.
Fatema
37
seu ponto é que os membros desse objeto podem não ser serializáveis, embora o tipo que o contém seja. certo? não é o caso de que devemos detalhar recursivamente os membros desses objetos e verificar cada um, se não apenas tentar serializá-lo e ver se ele falha?
Brian Sweeney
3
Por exemplo, para um List <SomeDTO> o IsSerializable é verdadeiro mesmo se SomeDTO NÃO for serializável
Simon Dowdeswell
43

Você terá que verificar todos os tipos no gráfico de objetos sendo serializados para o atributo serializável. A maneira mais fácil é tentar serializar o objeto e capturar a exceção. (Mas essa não é a solução mais limpa). Type.IsSerializable e a verificação do atributo serializalbe não levam o gráfico em consideração.

Amostra

[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
fonte
Se o custo não for muito alto, acho que essa abordagem é a melhor. Ele pode verificar diferentes requisitos de serialização (binário, xml). Além disso, um objeto pode ter um membro genérico que pode ser trocado por tipos de classe herdados que podem interromper a serialização e podem ser alterados no tempo de execução. List (Of baseclass) pode ter itens adicionados da subclasseA que não é serializável, onde a baseclass e a subclasse B são serializáveis.
VoteCoffee
Esta resposta usa clonagem para verificar se a serialização pode ser rodada. Pode ser exagero em alguns casos, embora a serialização não
deva
18

Esta é uma questão antiga, que pode precisar ser atualizada para .NET 3.5+. Type.IsSerializable pode realmente retornar false se a classe usar o atributo DataContract. Aqui está um trecho que eu uso, se fede, me avise :)

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

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

}
Mike_G
fonte
1
Pergunta antiga e respostas antigas, mas isso é MUITO verdade! Type.IsSerializable é apenas uma solução parcialmente funcional. Na verdade, considerando-se quantos usam WCF e DataContracts atualmente, é uma solução muito ruim!
Jaxidian
E se obj vier como nulo?
N73k
@ N73k fazer uma nullverificação e retornar se true?
FredM
9

Use Type.IsSerializable como outros apontaram.

Provavelmente não vale a pena tentar refletir e verificar se todos os membros no gráfico do objeto são serializáveis.

Um membro pode ser declarado como um tipo serializável, mas na verdade ser instanciado como um tipo derivado que não é serializável, como no seguinte exemplo inventado:

[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

Portanto, mesmo se você determinar que uma instância específica do seu tipo é serializável, em geral não pode ter certeza de que isso será verdadeiro para todas as instâncias.

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

Provavelmente envolve reflexão subaquática, mas da maneira mais simples?

Grad van Horck
fonte
5

Aqui está uma variação do 3.5 que o torna disponível para todas as classes usando um método de extensão.

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

Peguei a resposta a esta pergunta e a resposta aqui e modifiquei para obter uma lista de tipos que não são serializáveis. Dessa forma, você pode saber facilmente quais 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))));
    }

E então você chama isso ...

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

Quando for executado, nonSerializableTypes terá a lista. Pode haver uma maneira melhor de fazer isso do que passar uma lista vazia para o método recursivo. Alguém me corrija se for assim.

esgoto
fonte
0

O objeto de exceção pode ser serializável, mas usando uma outra exceção que não é. Isso é o que acabei de obter com WCF System.ServiceModel.FaultException: FaultException é serializável, mas ExceptionDetail não!

Portanto, estou usando o seguinte:

// 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
fonte
0

Minha solução, em 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
fonte