Comment vérifier si un objet est sérialisable en C #

94

Je recherche un moyen simple de vérifier si un objet en C # est sérialisable.

Comme nous le savons, vous rendez un objet sérialisable en implémentant l' interface ISerializable ou en plaçant le [Serializable] en haut de la classe.

Ce que je recherche, c'est un moyen rapide de vérifier cela sans avoir à refléter la classe pour obtenir ses attributs. L'interface serait rapide en utilisant une instruction is .

En utilisant la suggestion de @ Flard, c'est le code que j'ai trouvé, criez qu'il y a un meilleur moyen.

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

Ou mieux encore, obtenez simplement le type de l'objet, puis utilisez la propriété IsSerializable sur le type:

typeof(T).IsSerializable

Rappelez-vous bien que cela ne semble concerner que la classe à laquelle nous avons affaire si la classe contient d'autres classes que vous voulez probablement toutes vérifier ou essayer de sérialiser et attendre les erreurs comme @pb l'a souligné.

FryHard
la source
1
Désolé, cela échoue lorsqu'un champ dans obj n'est pas sérialisable, voir mon exemple.
Paul van Brenk
Je pense que c'est une bien meilleure approche: stackoverflow.com/questions/236599/…
xero
La déclaration "vous rendez un objet sérialisable en implémentant l'interface ISerializable ou en plaçant le [Serializable] en haut de la classe" est fausse. Pour qu'un objet soit sérialisable, sa classe doit déclarer le SerializableAttribute. La mise en œuvre d'ISerializable vous donne seulement plus de contrôle sur le processus.
Mishax

Réponses:

115

Vous avez une belle propriété sur la Typeclasse appelée IsSerializable.

leppie
la source
7
Cela vous informera simplement si un attribut de Serializable est attaché à votre classe.
Fatema
37
son point est que les membres de cet objet peuvent ne pas être sérialisables même si le type contenant l'est. droite? n'est-ce pas le cas que nous devons explorer de manière récursive les membres des objets et vérifier chacun d'eux, sinon simplement essayer de le sérialiser et voir s'il échoue?
Brian Sweeney
3
Par exemple pour une liste <SomeDTO>, IsSerializable est vrai même si SomeDTO n'est PAS sérialisable
Simon Dowdeswell
43

Vous allez devoir vérifier tous les types dans le graphique des objets sérialisés pour l'attribut sérialisable. Le moyen le plus simple est d'essayer de sérialiser l'objet et d'attraper l'exception. (Mais ce n'est pas la solution la plus propre). Type.IsSerializable et la vérification de l'attribut serializalbe ne prennent pas en compte le graphe.

Échantillon

[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
la source
Si le coût n'est pas trop élevé, je pense que cette approche est la meilleure. Il peut vérifier différentes exigences de sérialisation (binaire, xml). En outre, un objet peut avoir un membre générique qui peut être échangé avec des types de classe hérités qui peuvent interrompre la sérialisation et peuvent changer au moment de l'exécution. La liste (de la classe de base) pourrait avoir des éléments ajoutés de la sous-classeA qui n'est pas sérialisable, où la classe de base et la sous-classeB sont sérialisables.
VoteCoffee
Cette réponse utilise le clonage pour vérifier si la sérialisation peut aller-retour. Cela peut être excessif dans certains cas, même si la sérialisation ne devrait pas définir certains membres: stackoverflow.com/questions/236599/…
VoteCoffee
18

Il s'agit d'une ancienne question qui devra peut-être être mise à jour pour .NET 3.5+. Type.IsSerializable peut en fait retourner false si la classe utilise l'attribut DataContract. Voici un extrait que j'utilise, si ça pue, faites le moi savoir :)

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

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

}
Mike_G
la source
1
Ancienne question et vieilles réponses mais c'est TRÈS vrai! Type.IsSerializable n'est qu'une solution partiellement fonctionnelle. En fait, étant donné le nombre qui utilise WCF et DataContracts ces jours-ci, c'est en fait une très mauvaise solution!
Jaxidian
Que faire si obj est nul?
N73k
@ N73k faire une nullvérification et retourner si true?
FredM
9

Utilisez Type.IsSerializable comme d'autres l'ont souligné.

Cela ne vaut probablement pas la peine d'essayer de réfléchir et de vérifier si tous les membres du graphe d'objets sont sérialisables.

Un membre peut être déclaré en tant que type sérialisable, mais en fait être instancié en tant que type dérivé qui n'est pas sérialisable, comme dans l'exemple artificiel suivant:

[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

Par conséquent, même si vous déterminez qu'une instance spécifique de votre type est sérialisable, vous ne pouvez pas en général être sûr que cela sera vrai pour toutes les instances.

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

Implique probablement une réflexion sous l'eau, mais le moyen le plus simple?

Grad van Horck
la source
5

Voici une variante 3.5 qui la rend disponible à toutes les classes en utilisant une méthode d'extension.

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

J'ai pris la réponse à cette question et la réponse ici et je l'ai modifiée pour que vous obteniez une liste des types qui ne sont pas sérialisables. De cette façon, vous pouvez facilement savoir lesquels marquer.

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

Et puis vous l'appelez ...

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

Lorsqu'il s'exécute, nonSerializableTypes aura la liste. Il peut y avoir une meilleure façon de faire cela que de passer une liste vide à la méthode récursive. Quelqu'un me corrige si oui.

égout
la source
0

L'objet d'exception peut être sérialisable, mais en utilisant une autre exception qui ne l'est pas. C'est ce que je viens d'avoir avec WCF System.ServiceModel.FaultException: FaultException est sérialisable mais ExceptionDetail ne l'est pas!

J'utilise donc ce qui suit:

// 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
la source
0

Ma solution, dans VB.NET:

Pour les objets:

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

Pour les types:

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