2

I am using a custom session provider in my web application which is inheriting SessionStateStoreProviderBase and overriding methods. Method which is used for getting data from session has a return type of SessionStateStoreData.

Currently binary serialization is being used for serializing and deserializing the data. I want to use json serialization instead of binary but not sure how to convert the session data (of type string after json serialization) to SessionStateStoreData type. Constructor of SessionStateStoreData type uses the SessionStateItemCollection type object which can be get from deserialize method of sessionstateitemcollection which takes only binary stream as input.

var ms = _serializedSessionData == null
            ? new MemoryStream()
            : new MemoryStream(_serializedSessionData);

        var sessionItems = new SessionStateItemCollection();

        if (ms.Length > 0)
        {
            var reader = new BinaryReader(ms);
            sessionItems = SessionStateItemCollection.Deserialize(reader);
        }

return new SessionStateStoreData(sessionItems,
          SessionStateUtility.GetSessionStaticObjects(context),
          _timeout);

I have attached the code which is used for binary deserialization. How to do the same thing for json serialized object?

Yogesh Rajput
  • 87
  • 1
  • 11
  • you might try to convert the `json` string to `MemoryStream`. – Amit Kumar Ghosh Jun 04 '15 at 10:13
  • yes, but in this case sessionitems will be json serialized string and not the actual object stored in session. – Yogesh Rajput Jun 04 '15 at 10:20
  • `SessionStateItemCollection.Deserialize` only accepts `BinaryReader`. – Amit Kumar Ghosh Jun 04 '15 at 10:37
  • Yes, that is the problem. I was asking if any alternate can be used. – Yogesh Rajput Jun 04 '15 at 11:57
  • Do you really need a `SessionStateItemCollection`, or is a collection implementing [`ISessionStateItemCollection`](https://msdn.microsoft.com/en-us/library/system.web.sessionstate.isessionstateitemcollection%28v=vs.110%29.aspx) sufficient? [The former](http://referencesource.microsoft.com/#System.Web/State/SessionStateItemCollection.cs) has a lot of logic specifically for incremental binary deserialization. – dbc Jun 04 '15 at 18:36

1 Answers1

3

A SessionStateItemCollection is very close to a Dictionary<string, object>, so you could serialize it as such. However, you will need to account for the possibility of a null key. A null key is allowed by SessionStateItemCollection but Dictionary<string, object> will throw an exception on a null key.

Since the values in a SessionStateItemCollection are untyped, you will also need a JSON serializer that supports serializing of type information of arbitrary types. Json.NET can do this. JavaScriptSerializer can as well. DataContractJsonSerializer is probably not appropriate since you must know all possible types in advance and pass them to the constructor as known types.

For the remainder of the answer I'll assume you have chosen Json.NET.

First, define the following intermediate class for serialization:

public class StringDictionaryWrapper
{
    public StringDictionaryWrapper()
    {
        Items = new Dictionary<string, object>();
    }

    // Holds the value of the item with a null key, if any.
    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto, DefaultValueHandling = DefaultValueHandling.Ignore)]
    public object NullItem { get; set; }

    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
    public Dictionary<string, object> Items { get; set; }
}

Then, to serialize a SessionStateItemCollection to a json string, do:

        var dict = new StringDictionaryWrapper { Items = sessionItems.Keys.OfType<string>().ToDictionary(key => key, key => sessionItems[key]), NullItem = sessionItems[null] };
        var json = JsonConvert.SerializeObject(dict, Formatting.Indented);

The JSON will look something like:

{
  "NullItem": 1123,
  "Items": {
    "foo": {
      "$type": "Question30640792.SomeClass, Tile",
      "SomeProperty": "foo2"
    },
    "TestClass": {
      "$type": "Question30640792.TestClass, Tile",
      "A": 101,
      "B": 102
    }
  }
}

To deserialize from a json string, do:

        var sessionItems = new SessionStateItemCollection();

        var dict = JsonConvert.DeserializeObject<StringDictionaryWrapper>(json);
        if (dict != null && dict.NullItem != null)
            sessionItems[null] = dict.NullItem;
        if (dict != null && dict.Items != null)
            foreach (var pair in dict.Items)
                sessionItems[pair.Key] = pair.Value;

Note that this will deserialize all the entire collection of session state items at once, whereas the built-in binary serialization uses on-demand deserialization for performance reasons. Thus you might see a performance hit. Also, since Dictionary<TKey, TValue> is unordered, the session items may not come back in the same order as they were originally. If that's a problem, you may need to create something like this custom proxy wrapper for your session item collection.

Finally, when using TypeNameHandling, do take note of this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json.

dbc
  • 104,963
  • 20
  • 228
  • 340