示例#1
0
        static Action <IJsonWriter, object> ResolveFormatter(Type type)
        {
            // Try `void FormatJson(IJsonWriter)`
            var formatJson = ReflectionInfo.FindFormatJson(type);

            if (formatJson != null)
            {
                if (formatJson.ReturnType == typeof(void))
                {
                    return((w, obj) => formatJson.Invoke(obj, new Object[] { w }));
                }
                if (formatJson.ReturnType == typeof(string))
                {
                    return((w, obj) => w.WriteStringLiteral((string)formatJson.Invoke(obj, new Object[] { })));
                }
            }

            var ri = ReflectionInfo.GetReflectionInfo(type);

            if (ri != null)
            {
                return(ri.Write);
            }
            else
            {
                return(null);
            }
        }
示例#2
0
        // Generates a function that when passed an object of specified type, renders it to an IJsonWriter
        public static Action <IJsonWriter, object> MakeFormatter(Type type)
        {
            var formatJson = ReflectionInfo.FindFormatJson(type);

            if (formatJson != null)
            {
                var method = new DynamicMethod("invoke_formatJson", null, new Type[] { typeof(IJsonWriter), typeof(Object) }, true);
                var il     = method.GetILGenerator();
                if (formatJson.ReturnType == typeof(string))
                {
                    // w.WriteStringLiteral(o.FormatJson())
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Unbox, type);
                    il.Emit(OpCodes.Call, formatJson);
                    il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral"));
                }
                else
                {
                    // o.FormatJson(w);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(type.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, type);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, formatJson);
                }
                il.Emit(OpCodes.Ret);
                return((Action <IJsonWriter, object>)method.CreateDelegate(typeof(Action <IJsonWriter, object>)));
            }
            else
            {
                // Get the reflection info for this type
                var ri = ReflectionInfo.GetReflectionInfo(type);
                if (ri == null)
                {
                    return(null);
                }

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

                // Cast/unbox the target object and store in local variable
                var locTypedObj = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
                il.Emit(OpCodes.Stloc, locTypedObj);

                // Get Invariant CultureInfo (since we'll probably be needing this)
                var locInvariant = il.DeclareLocal(typeof(IFormatProvider));
                il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
                il.Emit(OpCodes.Stloc, locInvariant);

                // These are the types we'll call .ToString(Culture.InvariantCulture) on
                var toStringTypes = new Type[] {
                    typeof(int), typeof(uint), typeof(long), typeof(ulong),
                    typeof(short), typeof(ushort), typeof(decimal),
                    typeof(byte), typeof(sbyte)
                };

                // Theses types we also generate for
                var otherSupportedTypes = new Type[] {
                    typeof(double), typeof(float), typeof(string), typeof(char)
                };

                // Call IJsonWriting if implemented
                if (typeof(IJsonWriting).IsAssignableFrom(type))
                {
                    if (type.IsValueType)
                    {
                        il.Emit(OpCodes.Ldloca, locTypedObj);
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Call, type.GetInterfaceMap(typeof(IJsonWriting)).TargetMethods[0]);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldloc, locTypedObj);
                        il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWriting", new Type[] { typeof(IJsonWriter) }));
                    }
                }

                // Process all members
                foreach (var m in ri.Members)
                {
                    // Dont save deprecated properties
                    if (m.Deprecated)
                    {
                        continue;
                    }

                    // Ignore write only properties
                    var pi = m.Member as PropertyInfo;
                    if (pi != null && pi.GetGetMethod(true) == null)
                    {
                        continue;
                    }

                    // Get the member type
                    var memberType = m.MemberType;

                    // Get the field/property value and store it in a local
                    LocalBuilder locValue = il.DeclareLocal(memberType);
                    il.Emit(type.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locTypedObj);
                    if (pi != null)
                    {
                        il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, pi.GetGetMethod(true));
                    }
                    if (m.Member is FieldInfo fi)
                    {
                        il.Emit(OpCodes.Ldfld, fi);
                    }
                    il.Emit(OpCodes.Stloc, locValue);

                    // A label for early exit if not writing this member
                    Label lblFinishedMember = il.DefineLabel();

                    // Helper to generate IL to write the key
                    void EmitWriteKey()
                    {
                        // Write the Json key
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldstr, m.JsonKey);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteKeyNoEscaping", new Type[] { typeof(string) }));
                    }

                    // Is it a nullable type?
                    var typeUnderlying = Nullable.GetUnderlyingType(memberType);
                    if (typeUnderlying != null)
                    {
                        // Define some labels
                        var lblHasValue = il.DefineLabel();

                        // Call HasValue
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetProperty("HasValue").GetGetMethod());
                        il.Emit(OpCodes.Brtrue, lblHasValue);

                        // HasValue returned false, so either omit the key entirely, or write it as "null"
                        if (!m.ExcludeIfNull)
                        {
                            // Write the key
                            EmitWriteKey();

                            // No value, write "null"
                            il.Emit(OpCodes.Ldarg_0);
                            il.Emit(OpCodes.Ldstr, "null");
                            il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                        }
                        il.Emit(OpCodes.Br_S, lblFinishedMember);

                        // Get it's value
                        il.MarkLabel(lblHasValue);
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetProperty("Value").GetGetMethod());

                        // Switch to the underlying type from here on
                        locValue = il.DeclareLocal(typeUnderlying);
                        il.Emit(OpCodes.Stloc, locValue);
                        memberType = typeUnderlying;
                    }
                    else
                    {
                        if (m.ExcludeIfNull && !type.IsValueType)
                        {
                            il.Emit(OpCodes.Ldloc, locValue);
                            il.Emit(OpCodes.Brfalse_S, lblFinishedMember);
                        }
                    }

                    if (m.ExcludeIfEquals != null)
                    {
                        il.Emit(OpCodes.Ldloc, locValue);
                        var targetValue = Convert.ChangeType(m.ExcludeIfEquals, m.MemberType);
                        LoadContantFromObject(il, targetValue);
                        il.Emit(OpCodes.Ceq);
                        il.Emit(OpCodes.Brtrue_S, lblFinishedMember);
                    }

                    // ToString()
                    if (toStringTypes.Contains(memberType))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(memberType.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Ldloc, locInvariant);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(IFormatProvider) }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // ToString("R")
                    else if (memberType == typeof(float) || memberType == typeof(double))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(memberType.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Ldstr, "R");
                        il.Emit(OpCodes.Ldloc, locInvariant);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(string), typeof(IFormatProvider) }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // String?
                    else if (memberType == typeof(string))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
                    }

                    // Char?
                    else if (memberType == typeof(char))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
                    }

                    // Bool?
                    else if (memberType == typeof(bool))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        var lblTrue = il.DefineLabel();
                        var lblCont = il.DefineLabel();
                        il.Emit(OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Brtrue_S, lblTrue);
                        il.Emit(OpCodes.Ldstr, "false");
                        il.Emit(OpCodes.Br_S, lblCont);
                        il.MarkLabel(lblTrue);
                        il.Emit(OpCodes.Ldstr, "true");
                        il.MarkLabel(lblCont);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // NB: We don't support DateTime as it's format can be changed

                    else
                    {
                        // Load writer
                        il.Emit(OpCodes.Ldarg_0);

                        // Load value
                        il.Emit(OpCodes.Ldloc, locValue);
                        if (memberType.IsValueType)
                        {
                            il.Emit(OpCodes.Box, memberType);
                        }

                        // Write the key and value
                        if (m.ExcludeIfEmpty)
                        {
                            il.Emit(OpCodes.Ldstr, m.JsonKey);
                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("WriteKeyAndValueCheckIfEmpty"));
                        }
                        else
                        {
                            EmitWriteKey();
                            il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteValue", new Type[] { typeof(object) }));
                        }
                    }

                    il.MarkLabel(lblFinishedMember);
                }

                // Call IJsonWritten
                if (typeof(IJsonWritten).IsAssignableFrom(type))
                {
                    if (type.IsValueType)
                    {
                        il.Emit(OpCodes.Ldloca, locTypedObj);
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Call, type.GetInterfaceMap(typeof(IJsonWritten)).TargetMethods[0]);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldloc, locTypedObj);
                        il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWritten", new Type[] { typeof(IJsonWriter) }));
                    }
                }

                // Done!
                il.Emit(OpCodes.Ret);
                var impl = (Action <IJsonWriter, object>)method.CreateDelegate(typeof(Action <IJsonWriter, object>));

                // Wrap it in a call to WriteDictionary
                return((w, obj) =>
                {
                    w.WriteDictionary(() =>
                    {
                        impl(w, obj);
                    });
                });
            }
        }