45

My generic method needs to serialize the object passed to it, however just insisting that it implements ISerializable doesn't seem to work. For example, I have a struct returned from a web service (marked with SerializableAttribute) that serializes to xml just fine, but, as expected, the C# compiler complains.

Is there a way I can check the object is serializable before attempting to serialize it, or, better still, a way of using the where keyword to check the object is suitable?

Here's my full method:

public static void Push<T>(string url, T message)
        where T : ISerializable
{
    string xml = SerializeMessage(message);

    // Send the message to Amazon SQS
    SendMessageRequest sendReq = new SendMessageRequest { QueueUrl = url, MessageBody = xml };
    AmazonSQSClient client = new AmazonSQSClient(S3User, S3Pass);
    client.SendMessage(sendReq);
}

And SerializeMessage:

private static string SerializeMessage<T>(T message)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    using (StringWriter stringWriter = new StringWriter())
    {
        xmlSerializer.Serialize(stringWriter, message);
        return stringWriter.ToString();
    }
}

If this isn't possible, what's the best way to perform a check that an object is serializable at runtime?

Matt Brindley
  • 9,739
  • 7
  • 47
  • 51

5 Answers5

41

You can't do this totally via generic constraints, but you can do a couple things to help:

1) Put the new() constraint on the generic type (to enable the ability to deserialize and to ensure the XmlSerializer doesn't complain about a lack of default ctor):

where T : new()

2) On the first line of your method handling the serialization (or constructor or anywhere else you don't have to repeat it over and over), you can perform this check:

if( !typeof(T).IsSerializable && !(typeof(ISerializable).IsAssignableFrom(typeof(T)) ) )
    throw new InvalidOperationException("A serializable Type is required");

Of course, there's still the possibility of runtime exceptions when trying to serialize a type, but this will cover the most obvious issues.

Adam Sills
  • 16,896
  • 6
  • 51
  • 56
  • 4
    This answer is incorrect, as it will give false positives. typeof(T) evaluates to System.Runtime.Type, which is ISerializable, so it will always evaluate to true. Use this instead... if (!typeof(T).IsSerializable && !(typeof(ISerializable).IsAssignableFrom(typeof(T)))) – Daniel Dyson Jul 17 '12 at 10:33
  • 1
    where T : new() excludes System.Tuple<> which are marked with the Serializable attribute. – kerem Apr 16 '14 at 11:42
  • True, but that still seems pretty safe. Most people don't even know what a tuple is, let alone that there's a .NET Tuple class. Plus, Tuple will only serialize with binary serialization anyway (no XML) because it doesn't have a default ctor. – Adam Sills Apr 16 '14 at 16:35
11

I wrote a length blog article on this subject that you may find helpful. It mainly goes into binary serialization but the concepts are applicable to most any serialization format.

The long and short of it is

  • There is no way to add a reliable generic constraint
  • The only way to check and see if an object was serializable is to serialize it and see if the operation succeeds
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
4

The only way to know if an object is serializable is to try to serialize it.

In fact, you were asking how to tell if a type "is serializable", but the actual question will be with respect to objects. Some instances of a type may not be serializable even if the type is marked [Serializable]. For instance, what if the instance contains circular references?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
0

C# 8 and up allows the unmanaged constraint to limit types to structs that have nothing but value types in them (on any nested level). What we really want is:

public class MyClass<T> where T : ISerializable or unmanaged

But unfortunately, at the time of writing C# does not support this syntax (constraints are always AND, separated by commas).

A workaround could be a ValueWrapper class:

public class ValueWrapper<U> : ISerializable where U : unmanaged

This takes a U for a constructor argument. It has one property U Value. Now you can treat value types as ISerializable simply by wrapping them in a ValueWrapper.

Martin Maat
  • 714
  • 4
  • 23
0

Instead of

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

try

XmlSerializer xmlSerializer = new XmlSerializer(message.GetType());

Peter
  • 1,776
  • 13
  • 20