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

Deserialize anonymous types #53

Open NathanBaulch opened 10 years ago

NathanBaulch commented 10 years ago

I've written a serializer strategy that supports deserializing anonymous types and thought I'd share the code here for anyone who's interested. Anonymous types are immutable, with all property values passed in via constructor parameters. The strategy detects the absence of a default (parameterless) public constructor and builds the necessary cached ConstructorDelegate to create and populate the output object.

public static class SimpleJsonExt
{
    public static T DeserializeAnonymousObject<T>(string json, T prototype)
    {
        return SimpleJson.SimpleJson.DeserializeObject<T>(json, new Strategy());
    }

    private class Strategy : PocoJsonSerializerStrategy
    {
        private readonly IDictionary<Type, KeyValuePair<ParameterInfo[], ReflectionUtils.ConstructorDelegate>> _ctorCache = new ReflectionUtils.ThreadSafeDictionary<Type, KeyValuePair<ParameterInfo[], ReflectionUtils.ConstructorDelegate>>(CreateConstructorDelegate);

        private static KeyValuePair<ParameterInfo[], ReflectionUtils.ConstructorDelegate> CreateConstructorDelegate(Type key)
        {
            var ctors = key.GetConstructors();
            if (ctors.Length == 1)
            {
                var ctor = ctors[0];
                var parms = ctor.GetParameters();
                if (parms.Length > 0)
                {
                    return new KeyValuePair<ParameterInfo[], ReflectionUtils.ConstructorDelegate>(parms, ReflectionUtils.GetConstructor(ctor));
                }
            }
            return default(KeyValuePair<ParameterInfo[], ReflectionUtils.ConstructorDelegate>);
        }

        public override object DeserializeObject(object value, Type type)
        {
            var dict = value as IDictionary<string, object>;
            if (dict != null)
            {
                var ctor = _ctorCache[type];
                if (ctor.Key != null)
                {
                    object arg;
                    return ctor.Value(ctor.Key.Select(param => DeserializeObject(dict.TryGetValue(param.Name, out arg) ? arg : null, param.ParameterType)).ToArray());
                }
            }
            return base.DeserializeObject(value, type);
        }
    }
}