facebook-csharp-sdk / simple-json

JSON library for .NET 2.0+/SL4+/WP7/WindowsStore with optional support for dynamic and DataContract
MIT License
380 stars 143 forks source link

Support serialization of Newtonsoft JSON.NET JObject #40

Open eduardocampano opened 10 years ago

eduardocampano commented 10 years ago

As the Newtonsoft JSON.NET JObject implements IEnumerable it is treated as an array for serialization instead of being serialized directly.

My issue came up in the following case: ASP.NET WebApi used JSON.NET to deserialize a complex JSON object during an API request. The deserialized type contains an IDictionary<string, object> property to allow custom info to be sent to the API. As part of the WebApi deserialization all the entries in the dictionary had JObject instance values. Trying to serialize the type instance again using SimpleJson the dictionary is not correctly serialized.

Thanks, Eduardo

prabirshrestha commented 10 years ago

can u post a sample code of class and json you are trying to serialize/deserialize.

eduardocampano commented 10 years ago

I just pushed 2 failing unit tests to my fork https://github.com/eduardocampano/simple-json/blob/f7f9238820b21663b7e38acb8fb66eb7b57c0e72/src/SimpleJson.Tests/SerializeObject.JsonNet.Tests.cs I tried using TrySerializeNonPrimitiveObject in the IJsonSerializerStrategy but the code tries to serialize them as arrays because they implement IEnumerable.

prabirshrestha commented 10 years ago

Why would you want to use both at the same time in the same code? The only place where you would want to use both at the same place would be server side using webapi and in client use simplejson.

eduardocampano commented 10 years ago

In this case I'm building a logging library which serializes objects using SimpleJson. A consumer of this library is using WebApi with the default Json.Net serializer and sending the objects to log to my library. For now I provided him a SimpleJsonFormatter for WebApi and it solved the particular issue. But I would not like to force the library consumers to make this kind of changes. Maybe the solution is to have a hook where I can define a TypeConverter like Json.Net to force a type to use a specific converter before evaluating the default ones, as JObject is not able to reach the TrySerializeNonPrimitiveObject method.

prabirshrestha commented 10 years ago

You might be interested in this discussion. https://twitter.com/PrabirShrestha/status/271392381226590209

Since JObject is IDictionary<string, JToken> we might need to check if it contains IDictionary<string, TAnything> then treat it as a json object. Or just try to cast it as IDictionary. Not sure if this works in all version of PCL.

eduardocampano commented 10 years ago

Right, that could help, I will give that a try, thanks

eduardocampano commented 10 years ago

Ok, I'm back on this after a while, casting as IDictionary doesn't work because IDictionary<,> does not implement it. I came up with this function

public bool IsIEnumerableKeyValuePairOfStringAndAnything(Type type)
{
    foreach (var interfaceType in type.GetInterfaces())
    {
        if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            var genericArgument = interfaceType.GetGenericArguments()[0]; 
            if (genericArgument.IsGenericType && genericArgument.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)
                && genericArgument.GetGenericArguments()[0] == typeof(string))
            return true;
        }
    }

    return false;
}

It will check if the type to be serialized is an IEnumerable<KeyValuePair<string, TAnything>> so it can be treated as json object as you suggest. The current checks for IDictionary<string, object> and IDictionary<string, string> won't be necessary anymore. I haven't check its performance yet considering it will be executed a lot, results must be cached.

What do you think?