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

Some deserialization workarounds (DateTime, Enum, Lists) #78

Open hardcodet opened 7 years ago

hardcodet commented 7 years ago

Since the lib doesn't seem to be supported anymore, here's a few fixes and workarounds I added to get me going to deserialize some simple DTOs that were created through Json.net:

DateTime/DateTimeOffset parsing

The format used by Json.net caused me exceptions when trying to deserialize them. I replaced the DateTime/DateTimeOffset parsing as follows, which makes Json.net's IsoDateTimeConverter:

return DateTime.Parse(str, CultureInfo.CurrentCulture, DateTimeStyles.RoundtripKind);

Enum deserialization

In DeserializeObject, I added this (before the string evaluation):

if (type.GetTypeInfo().IsEnum)
{
    if (value is double || value is int || value is long)
    {
        return Enum.ToObject(type, Convert.ToInt32(value.ToString()));
    }

    if (str != null)
    {
        return Enum.Parse(type, value.ToString());
    }
}

List instantiation handling

That looked like a bug and crashed on me at runtime. The parser seems to try and create lists with a capacity parameter, but it appears that at runtime, the wrong constructor is invoked, me a TargetParameterCountException. In DeserializeObject, find the line where the lists are being created and just remove the constructor parameter like so:

Type innerType = ReflectionUtils.GetGenericListElementType(type);
list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])();

Then make sure the constructor cache works without parameters by adjusting this method and removing the distinction between arrays and other types:

internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key)
{
    return ReflectionUtils.GetContructor(key, EmptyTypes);
}
ghost commented 4 years ago

List instantiation handling:

Alternatively you can update ContructorDelegateFactory to check whether the type is an IList:

internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key)
{
    bool isArray = key.IsArray || typeof(IList).IsAssignableFrom(key);
    return ReflectionUtils.GetContructor(key, isArray ? ArrayConstructorParameterTypes : EmptyTypes);
}