Ejemplo n.º 1
0
        // Make a parser for value types
        public static Func <IJsonReader, Type, object> MakeParser(Type type)
        {
            System.Diagnostics.Debug.Assert(type.IsValueType);

            // ParseJson method?
            var parseJson = ReflectionInfo.FindParseJson(type);

            if (parseJson != null)
            {
                if (parseJson.GetParameters()[0].ParameterType == typeof(IJsonReader))
                {
                    var method = new DynamicMethod("invoke_ParseJson", typeof(Object), new Type[] { typeof(IJsonReader), typeof(Type) }, true);
                    var il     = method.GetILGenerator();

                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, parseJson);
                    il.Emit(OpCodes.Box, type);
                    il.Emit(OpCodes.Ret);
                    return((Func <IJsonReader, Type, object>)method.CreateDelegate(typeof(Func <IJsonReader, Type, object>)));
                }
                else
                {
                    var method = new DynamicMethod("invoke_ParseJson", typeof(Object), new Type[] { typeof(string) }, true);
                    var il     = method.GetILGenerator();

                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, parseJson);
                    il.Emit(OpCodes.Box, type);
                    il.Emit(OpCodes.Ret);
                    var invoke = (Func <string, object>)method.CreateDelegate(typeof(Func <string, object>));

                    return((r, t) =>
                    {
                        if (r.GetLiteralKind() == LiteralKind.String)
                        {
                            var o = invoke(r.GetLiteralString());
                            r.NextToken();
                            return o;
                        }
                        throw new InvalidDataException(string.Format("Expected string literal for type {0}", type.FullName));
                    });
                }
            }
            else
            {
                // Get the reflection info for this type
                var ri = ReflectionInfo.GetReflectionInfo(type);
                if (ri == null)
                {
                    return(null);
                }

                // We'll create setters for each property/field
                var setters = new Dictionary <string, Action <IJsonReader, object> >();

                // Store the value in a pseudo box until it's fully initialized
                var boxType = typeof(PseudoBox <>).MakeGenericType(type);

                // Process all members
                foreach (var m in ri.Members)
                {
                    // Ignore write only properties
                    var pi = m.Member as PropertyInfo;
                    var fi = m.Member as FieldInfo;
                    if (pi != null && pi.GetSetMethod(true) == null)
                    {
                        continue;
                    }

                    // Create a dynamic method that can do the work
                    var method = new DynamicMethod("dynamic_parser", null, new Type[] { typeof(IJsonReader), typeof(object) }, true);
                    var il     = method.GetILGenerator();

                    // Load the target
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Castclass, boxType);
                    il.Emit(OpCodes.Ldflda, boxType.GetField("value"));

                    // Get the value
                    GenerateGetJsonValue(m, il);

                    // Assign it
                    if (pi != null)
                    {
                        il.Emit(OpCodes.Call, pi.GetSetMethod(true));
                    }
                    if (fi != null)
                    {
                        il.Emit(OpCodes.Stfld, fi);
                    }

                    // Done
                    il.Emit(OpCodes.Ret);

                    // Store in the map of setters
                    setters.Add(m.JsonKey, (Action <IJsonReader, object>)method.CreateDelegate(typeof(Action <IJsonReader, object>)));
                }

                // Create helpers to invoke the interfaces (this is painful but avoids having to really box
                // the value in order to call the interface).
                Action <object, IJsonReader>             invokeLoading = MakeInterfaceCall(type, typeof(IJsonLoading));
                Action <object, IJsonReader>             invokeLoaded  = MakeInterfaceCall(type, typeof(IJsonLoaded));
                Func <object, IJsonReader, string, bool> invokeField   = MakeLoadFieldCall(type);

                // Create the parser
                Func <IJsonReader, Type, object> parser = (reader, Type) =>
                {
                    // Create pseudobox (ie: new PseudoBox<Type>)
                    var box = DecoratingActivator.CreateInstance(boxType);

                    // Call IJsonLoading
                    if (invokeLoading != null)
                    {
                        invokeLoading(box, reader);
                    }

                    // Read the dictionary
                    reader.ParseDictionary(key =>
                    {
                        // Call IJsonLoadField
                        if (invokeField != null && invokeField(box, reader, key))
                        {
                            return;
                        }

                        // Get a setter and invoke it if found
                        Action <IJsonReader, object> setter;
                        if (setters.TryGetValue(key, out setter))
                        {
                            setter(reader, box);
                        }
                    });

                    // IJsonLoaded
                    if (invokeLoaded != null)
                    {
                        invokeLoaded(box, reader);
                    }

                    // Return the value
                    return(((IPseudoBox)box).GetValue());
                };

                // Done
                return(parser);
            }
        }