3

BinaryFormatter is able to handle serialization simply:

private byte[] TokenToBytes(SessionSecurityToken token)
{
    if (token == null)
    {
        return null;
    }

    using (var memoryStream = new MemoryStream())
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, token);
        return memoryStream.ToArray();
    }
}

When I tried replacing BinaryFormatter with protobuf-net:

using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, token);
    return memoryStream.ToArray();
}

I get the following exception:

Type is not expected, and no contract can be inferred: System.IdentityModel.Tokens.SessionSecurityToken

I tried adding:

RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), true);

Which gets past the exception but I now get a zero byte array.

How do I properly configure protobuf-net to serialize a SessionSecurityToken?

On the flipside, SessionSecurityToken does not have a parameterless constructor.

using (var memoryStream = new MemoryStream(tokenAsBytes))
{
    return Serializer.Deserialize<SessionSecurityToken>(memoryStream);
}

Throws a ProtoException:

No parameterless constructor found for SessionSecurityToken

BinaryFormatter is able to do it without any fuss:

using (var memoryStream = new MemoryStream(bytes))
{
    var binaryFormatter = new BinaryFormatter();
    return (SessionSecurityToken)binaryFormatter.Deserialize(memoryStream);
}

How do I properly configure protobuf-net to deserialize a SessionSecurityToken?

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
David Peden
  • 17,596
  • 6
  • 52
  • 72
  • You are unable to serialize the SessionSecurityToken using protobuf cause it inherits SecurityToken which is not serializable. could you please tell me that can you convert SessionSecurityToken to some other type like byte[] or some generic type and can get back SessionSecurityToken form converted type? then i can give you a solution. @DPeden – Rezoan Jul 10 '13 at 07:14
  • can you create a new custom class with the member of SessionSecurityToken that can take all the necessary data from SessionSecurityToken members? – Rezoan Jul 10 '13 at 07:42
  • I have exactly the same problem, with another type - but the same exact problem - did you ever solve this? – VisualBean Aug 30 '13 at 11:39
  • I stayed with BinaryFormatter as it worked and, as Marc mentioned in his last sentence, it wasn't worth it to jump through the hoops since this was the only thing I was serializing, not a larger model. – David Peden Aug 30 '13 at 15:29

1 Answers1

4

protobuf-net does not claim to be able to serialize every single type; indeed, you would have great difficulty serializing that via most serializers (XmlSerializer, any of the json serializers, DataContractSerializer, etc). BinaryFormatter is in a different category of serializers - and in this particular case, implements custom serialization via ISerializable.GetObjectData(SerializationInfo, StreamingContext).

The constructor thing is a red herring; actually, protobuf-net can bypass constructors completely, and in this particular scenario BinaryFormatter is using a custom serialization constructor via .ctor(SerializationInfo, StreamingContext).

For simple cases, protobuf-net can be configured via attributes or runtime options; for more complex scenarios, surrogates can be used to map between representations - however, in this case I would suggest (looking at the implementation of SessionSecurityToken) that this is more complex than you probably want to maintain.

I would step back a step or two here; most serializers are designed to work with data, not implementation - and work great with DTOs etc. SessionSecurityToken is very much not a DTO, and there is no simple way of switching between them. My strong suggestion here would be: serialize what this represents, not what it is. However, if this is part of an existing complex model and is really really hard to separate out, you could switch back to BinaryFormatter for those bits. I haven't tested this, but consider:

RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), false)
        .SetSurrogate(typeof(BinaryFormatterSurrogate<SessionSecurityToken>));

With:

[ProtoContract]
public class BinaryFormatterSurrogate<T>
{
    [ProtoMember(1)]
    public byte[] Raw { get; set; }

    public static explicit operator T(BinaryFormatterSurrogate<T> value)
    {
        if(value==null || value.Raw == null) return default(T);
        using(var ms = new MemoryStream(value.Raw))
        {
            return (T)new BinaryFormatter().Deserialize(ms);
        }
    }
    public static explicit operator BinaryFormatterSurrogate<T>(T value)
    {
        object obj = value;
        if (obj == null) return null;
        using (var ms = new MemoryStream())
        {
            new BinaryFormatter().Serialize(ms, obj);
            return new BinaryFormatterSurrogate<T> { Raw = ms.ToArray() };
        }

    }
}

Keep in mind that this simply embeds the output of one serializer as raw data inside another. Fortunately protobuf-net is happy talking binary, so this won't add any noticeable overhead (just the header and length-prefix for the blob) - but it also won't do anything particularly smart or clever with the SessionSecurityToken instances. If this is the only thing you are serializing, it really isn't worth it. If this is just one ugly bump in a larger DTO model, where most of it can serialize nicely - then it might get the job done for you.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • thanks for taking the time to respond with some sample code. I was unable to look at this for about a week. Sorry for the slow response. – David Peden Jul 18 '13 at 14:58
  • Marc where do you put the calls to RuntimeTypeModel.Default.Add. Would you put them in the object constructor or in a static constructor for the entire project? Secondly what is the difference between ProtoBufFormatter.Model vs RuntimeTypeModel,Default. Is one for WebAPI and one is for the other nuget package? – rollsch Jul 26 '17 at 05:23