Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json.NET does not preserve primitive type information in lists or dictionaries of objects. Is there a workaround?

Tags:

json

c#

json.net

The following example illustrates a fundamental flaw in Json.NET's type handling:

List<object> items = new List<object>() {Guid.NewGuid(),DateTime.Now};
var settings = new JsonSerializerSettings() { TypeNameHandling=TypeNameHandling.All };
var json = JsonConvert.SerializeObject<List<object>>(value,settings);

resulting in the following JSON:

{"$type":"System.Collections.Generic.List`1[[System.Object, mscorlib]], mscorlib","$values":["9d7aa4d3-a340-4cee-baa8-6af0582b8acd","2014-07-28T21:03:17.1287029-04:00"]}

As you can see the list items have lost their type information. Deserializing that same JSON will result in a list containing just strings.

This issue was previously reported on codeplex and perfunctorily closed, stating including the type information would make the JSON too messy. I am surprised we aren't given a separate option to include primitive type information for such scenarios as the round-trip consistency is broken.

https://json.codeplex.com/workitem/23833

I would expect the data to come back with the same type information that it left with. Does anybody have any suggestions or workarounds to remedy this undesired behavior?

Thanks,

Chris

like image 324
Chris C Avatar asked Jul 29 '14 01:07

Chris C


2 Answers

Here is a solution using a custom JsonConverter:

public sealed class PrimitiveJsonConverter : JsonConverter
{
    public PrimitiveJsonConverter()
    {

    }

    public override bool CanRead
    {
        get
        {
            return false;
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsPrimitive;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        switch (serializer.TypeNameHandling)
        {
            case TypeNameHandling.All:
                writer.WriteStartObject();
                writer.WritePropertyName("$type", false);

                switch (serializer.TypeNameAssemblyFormat)
                {
                    case FormatterAssemblyStyle.Full:
                        writer.WriteValue(value.GetType().AssemblyQualifiedName);
                        break;
                    default:
                        writer.WriteValue(value.GetType().FullName);
                        break;
                }

                writer.WritePropertyName("$value", false);
                writer.WriteValue(value);
                writer.WriteEndObject();
                break;
            default:
                writer.WriteValue(value);
                break;
        }
    }
}

Here is how to use it:

JsonSerializerSettings settings = new JsonSerializerSettings() 
{ 
    TypeNameHandling = TypeNameHandling.All,
};

settings.Converters.Insert(0, new PrimitiveJsonConverter());
return JsonConvert.SerializeObject(myDotNetObject, settings);

I'm currently using this solution to serialize an IDictionary<string, object> instance that can contain primitives.

like image 88
Sacrilege Avatar answered Sep 22 '22 17:09

Sacrilege


Hacked this together and tested it out. Obviously this needs unit testing and is more a proof of concept. If you want a dirty solution to get you going this should get one started.

https://github.com/xstos/Newtonsoft.Json/commit/8d3507cbba78f7096a82e42973e56d69c9541c42

like image 39
Chris C Avatar answered Sep 23 '22 17:09

Chris C