Cómo comprobar si un objeto es serializable en C#
-
09-06-2019 - |
Pregunta
Estoy buscando una manera sencilla de comprobar si un objeto en C# es serializable.
Como sabemos, usted hace que un objeto sea serializable implementando el ISerializable interfaz o colocando el [Serializable] en la parte superior de la clase.
Lo que estoy buscando es una forma rápida de comprobar esto sin tener que reflejar la clase para obtener sus atributos.La interfaz sería rápida usando un es declaración.
Usando la sugerencia de @Flard, este es el código que se me ocurrió, grita 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 intentar serializarlas y esperar errores como señaló @pb.
Solución
Tienes una hermosa propiedad en el Type
clase llamada IsSerializable
.
Otros consejos
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 verificar el 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);
}
}
Esta es una pregunta antigua que quizás deba actualizarse 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, házmelo saber :)
public static bool IsSerializable(this object obj)
{
Type t = obj.GetType();
return Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)
}
Utilice Type.IsSerializable como otros han señalado.
Probablemente no valga la pena intentar reflexionar y comprobar si todos los miembros del gráfico de objetos son serializables.
Un miembro podría declararse como un tipo serializable, pero en realidad instanciarse como un tipo derivado que no es serializable, como en el siguiente ejemplo ideado:
[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 será cierto para todas las instancias.
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));
Probablemente implique reflexión bajo el agua, pero ¿la forma más sencilla?
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));
}
Tomé la respuesta a esta pregunta y la respuesta aquí y lo modifiqué para que obtenga una Lista de tipos que no son serializables.De esa manera podrás 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.Que alguien me corrija si es así.
El objeto de excepción puede ser serializable, pero utiliza 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);
}
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