I am looking for an easy way to check if an object in C# is serializable.
As we know you make an object serializable by either implementing the ISerializable
I took the answer on this question and the answer here and modified it so you get a List of types that aren't serializable. That way you can easily know which ones to mark.
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))));
}
And then you call it...
List<string> nonSerializableTypes = new List<string>();
NonSerializableTypesOfParentType(aType, nonSerializableTypes);
When it runs, nonSerializableTypes will have the list. There may be a better way of doing this than passing in an empty List to the recursive method. Someone correct me if so.
This is an old question, that may need to be updated for .NET 3.5+. Type.IsSerializable can actually return false if the class uses the DataContract attribute. Here is a snippet i use, if it stinks, let me know :)
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 as others have pointed out.
It's probably not worth attempting to reflect and check if all members in the object graph are serializable.
A member could be declared as a serializable type, but in fact be instantiated as a derived type that is not serializable, as in the following contrived example:
[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
Therefore, even if you determine that a specific instance of your type is serializable, you can't in general be sure this will be true of all instances.
The exception object might be serializable , but using an other exception which is not. This is what I just had with WCF System.ServiceModel.FaultException: FaultException is serializable but ExceptionDetail is not!
So I am using the following:
// 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);
}
My solution, in VB.NET:
For Objects:
''' <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
For 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
Here's a 3.5 variation that makes it available to all classes using an extension method.
public static bool IsSerializable(this object obj)
{
if (obj is ISerializable)
return true;
return Attribute.IsDefined(obj.GetType(), typeof(SerializableAttribute));
}