Come verificare se un oggetto è serializzabile in C #


94

Sto cercando un modo semplice per verificare se un oggetto in C # è serializzabile.

Come sappiamo, si rende serializzabile un oggetto implementando l' interfaccia ISerializable o posizionando [Serializable] all'inizio della classe.

Quello che sto cercando è un modo rapido per verificarlo senza dover riflettere la classe per ottenere i suoi attributi. L'interfaccia sarebbe rapida utilizzando un'istruzione is .

Usando il suggerimento di @ Flard questo è il codice che ho escogitato, urla se c'è un modo migliore.

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

O ancora meglio basta ottenere il tipo di oggetto e quindi utilizzare la proprietà IsSerializable sul tipo:

typeof(T).IsSerializable

Ricorda però che questo sembra solo per la classe con cui abbiamo a che fare se la classe contiene altre classi, probabilmente vorrai controllarle tutte o provare a serializzare e attendere gli errori come ha sottolineato @pb.


1
Ci dispiace che fallisca quando un campo in obj non è serializzabile, vedi il mio esempio.
Paul van Brenk

Penso che questo sia un approccio molto migliore: stackoverflow.com/questions/236599/…
xero

L'affermazione "rendi serializzabile un oggetto implementando l'interfaccia ISerializable o inserendo [Serializable] all'inizio della classe" è falsa. Affinché un oggetto sia serializzabile, la relativa classe deve dichiarare SerializableAttribute. L'implementazione di ISerializable offre solo un maggiore controllo sul processo.
Mishax

Risposte:


115

Hai una bella proprietà nella Typeclasse chiamata IsSerializable.


7
Questo ti informerà solo se un attributo di Serializable è allegato alla tua classe.
Fatema

37
il suo punto è che i membri di quell'oggetto potrebbero non essere serializzabili anche se il tipo che lo contiene è. giusto? non è forse il caso che dobbiamo analizzare in modo ricorsivo i membri degli oggetti e controllarli ciascuno, se non provare a serializzarli e vedere se fallisce?
Brian Sweeney

3
Ad esempio per un List <SomeDTO> IsSerializable è vero anche se SomeDTO NON è serializzabile
Simon Dowdeswell

43

Dovrai controllare tutti i tipi nel grafico degli oggetti serializzati per l'attributo serializzabile. Il modo più semplice è provare a serializzare l'oggetto e catturare l'eccezione. (Ma non è la soluzione più pulita). Type.IsSerializable e il controllo dell'attributo serializalbe non tengono conto del grafico.

Campione

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

Se il costo non è troppo elevato, penso che questo approccio sia il migliore. Può controllare diversi requisiti di serializzazione (binario, xml). Inoltre, un oggetto potrebbe avere un membro generico che può essere scambiato con tipi di classe ereditati che potrebbero interrompere la serializzazione e potrebbero cambiare in fase di esecuzione. List (Of baseclass) potrebbe avere elementi aggiunti della sottoclasseA che non è serializzabile, dove baseclass e sottoclassB sono serializzabili.
VoteCoffee

Questa risposta usa la clonazione per verificare se la serializzazione può essere andata e ritorno. Può essere eccessivo in alcuni casi, anche se sono stati serializzazione non si prevede di impostare alcuni membri: stackoverflow.com/questions/236599/...
VoteCoffee

18

Questa è una vecchia domanda, che potrebbe dover essere aggiornata per .NET 3.5+. Type.IsSerializable può effettivamente restituire false se la classe utilizza l'attributo DataContract. Ecco uno snippet che uso, se puzza, fammelo sapere :)

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

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

}

1
Vecchia domanda e vecchie risposte ma questo è MOLTO vero! Type.IsSerializable è solo una soluzione parzialmente funzionale. In effetti, dato quanti usano WCF e DataContracts in questi giorni, è in realtà una soluzione molto scarsa!
Jaxidian

E se obj si presenta come null?
N73k

@ N73k fai un nullcontrollo e restituisci se true?
FredM

9

Usa Type.IsSerializable come altri hanno sottolineato.

Probabilmente non vale la pena tentare di riflettere e controllare se tutti i membri nell'oggetto grafico sono serializzabili.

Un membro potrebbe essere dichiarato come tipo serializzabile, ma in realtà essere istanziato come tipo derivato non serializzabile, come nel seguente esempio inventato:

[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

Pertanto, anche se determini che un'istanza specifica del tuo tipo è serializzabile, in generale non puoi essere certo che ciò sia vero per tutte le istanze.


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

Probabilmente implica la riflessione sott'acqua, ma il modo più semplice?


5

Ecco una variazione 3.5 che lo rende disponibile a tutte le classi utilizzando un metodo di estensione.

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

2

Ho preso la risposta a questa domanda e la risposta qui e l'ho modificata in modo da ottenere un elenco di tipi che non sono serializzabili. In questo modo puoi sapere facilmente quali contrassegnare.

    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 poi lo chiami ...

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

Quando viene eseguito, nonSerializableTypes avrà l'elenco. Potrebbe esserci un modo migliore per farlo piuttosto che passare un elenco vuoto al metodo ricorsivo. Qualcuno mi corregga se è così.


0

L'oggetto eccezione potrebbe essere serializzabile, ma utilizzando un'altra eccezione che non lo è. Questo è quello che ho appena avuto con WCF System.ServiceModel.FaultException: FaultException è serializzabile ma ExceptionDetail no!

Quindi sto usando quanto segue:

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

La mia soluzione, in VB.NET:

Per gli oggetti:

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

Per i tipi:

''' <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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.