Je cherche un moyen simple de vérifier si un objet en C # est sérialisable.
Comme nous le savons, vous pouvez rendre un objet sérialisable en implémentant l'interface ISerializable ou en plaçant [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é, crier est qu'il y a une meilleure façon.
private static bool IsSerializable(T obj)
{
return ((obj is ISerializable) || (Attribute.IsDefined(typeof (T), typeof (SerializableAttribute))));
}
Ou encore mieux, obtenez simplement le type de l'objet, puis utilisez la propriété IsSerializable sur le type:
typeof(T).IsSerializable
Rappelez-vous que cela semble ne concerner que la classe à laquelle nous avons affaire si la classe contient d'autres classes que vous voulez probablement toutes vérifier ou essayez de sérialiser et d'attendre les erreurs comme l'a souligné @pb.
Vous avez une jolie propriété sur la classe Type
appelée IsSerializable
.
Vous devrez vérifier tous les types dans le graphique des objets en cours de sérialisation pour l'attribut sérialisable. La manière la plus simple est d'essayer de sérialiser l'objet et de capturer 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 graphique.
exemple
[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);
}
}
Il s'agit d'une ancienne question, qui peut nécessiter une mise à jour pour .NET 3.5+. Type.IsSerializable peut réellement retourner false si la classe utilise l'attribut DataContract. Voici un extrait que j'utilise, s'il 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)
}
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'objet sont sérialisables.
Un membre peut être déclaré comme un type sérialisable, mais en fait être instancié comme un 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 généralement pas être sûr que ce sera le cas pour toutes les instances.
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));
Implique probablement une réflexion sous l'eau, mais le moyen le plus simple?
Voici une variante 3.5 qui le rend disponible pour toutes les classes 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));
}
J'ai pris la réponse à cette question et la réponse ici et l'ai modifiée pour que vous obteniez une liste de 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 procéder que de passer une liste vide à la méthode récursive. Quelqu'un me corrige si c'est le cas.
Ma solution, en 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
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);
}