Como verificar se um objeto é serializável em C#
-
09-06-2019 - |
Pergunta
Estou procurando uma maneira fácil de verificar se um objeto em C# é serializável.
Como sabemos, você torna um objeto serializável implementando o ISerializável interface ou colocando o [Serializável] no topo da classe.
O que estou procurando é uma maneira rápida de verificar isso sem precisar refletir a classe para obter seus atributos.A interface seria rápida usando um é declaração.
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, basta obter o tipo do objeto e usar a propriedade IsSerializable no tipo:
typeof(T).IsSerializable
Lembre-se, porém, de que isso parece ser apenas a classe com a qual estamos lidando, se a classe contiver outras classes, você provavelmente deseja verificar todas elas ou tentar serializar e aguardar erros, como @pb apontou.
Solução
Você tem uma bela propriedade na classe Type
chamado IsSerializable
.
Outras dicas
Você vai ter que verificar todos os tipos no gráfico de objetos sendo serializado para o atributo serializable. A maneira mais fácil é tentar serializar o objeto e capturar a exceção. (Mas isso não é a solução mais limpa). Type.IsSerializable e verificação para o atributo serializalbe não tomar o gráfico em conta.
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);
}
}
Esta é uma velha questão, que pode precisar ser atualizado para .NET 3.5+. Type.IsSerializable pode realmente retornar false se a classe usa o atributo DataContract. Aqui está um trecho eu uso, se ele fede, deixe-me saber:)
public static bool IsSerializable(this object obj)
{
Type t = obj.GetType();
return Attribute.IsDefined(t, typeof(DataContractAttribute)) || t.IsSerializable || (obj is IXmlSerializable)
}
Use Type.IsSerializable como outros apontaram.
Provavelmente não vale a pena tentar refletir e verificar se todos os membros do 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 exemplo inventado seguinte:
[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 de seu tipo é serializável, você não pode nos ser geral certeza que isso vai ser verdade para todas as instâncias.
Attribute.IsDefined(typeof (YourClass), typeof (SerializableAttribute));
Provavelmente envolve reflexão subaquática, mas a forma mais simples?
Aqui está uma variação 3.5 que 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));
}
Eu levei a resposta sobre esta questão ea resposta aqui e modificado de modo a obter uma lista de tipos que não são serializado. Dessa forma, você pode facilmente saber quais marca.
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ê chamá-lo ...
List<string> nonSerializableTypes = new List<string>();
NonSerializableTypesOfParentType(aType, nonSerializableTypes);
Quando ele é executado, nonSerializableTypes terá a lista. Pode haver uma maneira melhor de fazer isso do que passar em uma lista vazia para o método recursiva. Alguém me corrija se assim.
O objeto de exceção pode ser serializável, mas utilizando uma outra exceção que não é. Isto é o que eu tinha com o WCF System.ServiceModel.FaultException: FaultException é serializável, mas ExceptionDetail não é
Então, eu 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);
}
A 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