3

I have a class with some data I'm serializing:

[Serializable]
public class SomeData {
    public int NumericField;
    public string StringField;
}

I have a nice convenient extension method:

public static string ToJson<T> (this T value) {
    // yay no compile-time checks for [Serializable]?
    if (!typeof(T).IsSerializable)
        throw new SerializationException("Object lacks [Serializable] attribute");

    var serializer = new DataContractJsonSerializer(typeof(T));

    using (var stream = new MemoryStream()) {
        using (var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8)) {
            serializer.WriteObject(writer, value);
        }

        return Encoding.UTF8.GetString(stream.ToArray());
    }
}

I'd really rather my class SomeData implemented ISerializable, but when I add it, I'm told I need to add a method:

error CS0535: 'SomeData' does not implement interface member
System.Runtime.Serialization.ISerializable.GetObjectData(
System.Runtime.Serialization.SerializationInfo, 
System.Runtime.Serialization.StreamingContext)'

The problem is just that I want it to do exactly what it currently does, but also restrict ToJson<T> to ISerializable classes so no one will ever accidentally pass it something that would throw an avoidable exception. Like this:

public static string ToJson<T> (this T value) where T : ISerializable

Is there a concise implementation of GetObjectData or some other trick that will just replicate the exact behavior I would get if I don't add the ISerializable interface? I want something I can just include in a class I inherit, or something concise (and always exactly the same) I can just paste into all the classes where I just want exactly what [Serializable] does to happen. Something generic. Performance is not a huge concern, but I can only use .Net Framework 3.5.

UPDATE:

I'm perfectly willing to use [DataContract] in the place of [Serializable], but if I did that, is there a generic way I could implement ISerializable so it just does exactly what [DataContract] would do if I hadn't implemented ISerializable? (same qn as above with DataContract everywhere you see Serializable)

fcrick
  • 484
  • 5
  • 12
  • I did think of one solution that gets me partway - I could just add an empty interface to all my `[Serializable]` classes and just restrict on that, even if it's empty. I guess that won't expose my extension method to any ISerializable classes that happen to be about, though, which would be nice... – fcrick Apr 22 '12 at 06:06
  • @caesay this does not work - this syntax applies both constraints, not one OR the other. This can kinda work if you make two extension methods with different names, one constrained on `ISerializable` and the other on `CustomInterface`, but again it's not really what I want. – fcrick Apr 22 '12 at 07:37

3 Answers3

6

You don't need ISerializable at all, nor do you need to throw the SerializationException yourself. DataContractJsonSerializer would throw an exception if the type is not serializable. There are actually several options for a type to be serializable using this serializer:

  • Serializable attribute
  • DataContract attribute
  • ISerializable interface
  • Nothing (would work for public types and serialize all public fields and properties)

Letting the serializer perform the check would support all of them.

There could not be a compile-time check for serializability (without investing in a Roslyn analyzer that provides some supports for this). Marking an object as Serializable or even implementing the ISerializable interface does not guarantee that the serializer wouldn't encounter an non-serializable type in the object graph and throw an exception at runtime.

Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • This doesn't actually answer my question, but I may remove `[Serializable]` until I have a solution (didn't realize I could omit it). The point is to enforce an appropriate compile time check, and also not have auto-complete suggest my extension method for everything all the time. – fcrick Apr 22 '12 at 08:13
  • also, why would the `DataContractJsonSerializer` ever throw an exception? – fcrick Apr 22 '12 at 08:14
  • So because the compile-time check won't prevent all possible exceptions, I should get rid of it even if it catches the errors I want to detect? I understand that `DataContract` and `DataMember` are alternatives to `Serializable`, but they'd leave me with the exact same question, substituting `DataContract` in the place of `Serializable`. (I'd be happy with that answer, too, incidentally) – fcrick Apr 22 '12 at 20:45
  • How could I do that, Eli? isn't `[Serializable]` the marker interface, and can't be checked at compile time? – fcrick Apr 23 '12 at 20:54
0
  1. As you yourself commented, add a blank interface and put it as a constraint in your extension method. You can also add ISerializable as a constraint C# generics syntax for multiple type parameter constraints

  2. Another option is to inherit a base class that implements ISerializable and the required methods

Community
  • 1
  • 1
Pinakin Shah
  • 877
  • 2
  • 12
  • 25
  • 1
    Also, the question you linked to isn't about adding multiple constraints on a single type, but rather how to add constraints when you have multiple type parameters... – fcrick Apr 22 '12 at 07:28
0

To work around this behavior, implement the ISerializable.GetObjectData method in the converted Visual C# .NET class. To do this, add the following code in the converted Visual C# .NET class:

void System.Runtime.Serialization.ISerializable.GetObjectData(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
{
    // Code as required
}
kawa
  • 422
  • 4
  • 16