Browse Source

Big code clean up

Brad Robinson 11 năm trước cách đây
mục cha
commit
d3a2e8d47b
6 tập tin đã thay đổi với 612 bổ sung359 xóa
  1. 242 245
      PetaJson.cs
  2. 298 110
      PetaJsonEmit.cs
  3. 0 3
      TestCases/Program.cs
  4. 1 0
      TestCases/TestCases.csproj
  5. 70 0
      TestCases/TestsEvents.cs
  6. 1 1
      readme.md

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 242 - 245
PetaJson.cs


+ 298 - 110
PetaJsonEmit.cs

@@ -13,8 +13,9 @@ namespace PetaJson
     {
         public static void Init()
         {
-            Json.SetTypeFormatterResolver(Internal.Emit.MakeFormatter);
-            Json.SetTypeIntoParserResolver(Internal.Emit.MakeIntoParser);
+            Json.SetFormatterResolver(Internal.Emit.MakeFormatter);
+            Json.SetParserResolver(Internal.Emit.MakeParser);
+            Json.SetIntoParserResolver(Internal.Emit.MakeIntoParser);
         }
     }
 
@@ -22,6 +23,8 @@ namespace PetaJson
     {
         static class Emit
         {
+
+            // Generates a function that when passed an object of specified type, renders it to an IJsonReader
             public static Action<IJsonWriter, object> MakeFormatter(Type type)
             {
                 // Get the reflection info for this type
@@ -39,7 +42,7 @@ namespace PetaJson
                 il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
                 il.Emit(OpCodes.Stloc, locTypedObj);
 
-                // Get Invariant CultureInfo
+                // 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);
@@ -59,12 +62,19 @@ namespace PetaJson
                 // Call IJsonWriting if implemented
                 if (typeof(IJsonWriting).IsAssignableFrom(type))
                 {
-                    il.Emit(OpCodes.Ldloc, locTypedObj);
                     if (type.IsValueType)
-                        il.Emit(OpCodes.Box, type);
-                    il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
-                    il.Emit(OpCodes.Ldarg_0);
-                    il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWriting", new Type[] { typeof(IJsonWriter) }));
+                    {
+                        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
@@ -98,7 +108,7 @@ namespace PetaJson
                         il.Emit(OpCodes.Ldloc, locTypedObj);
                     }
 
-                    // Work out if we need to the value or it's address on the stack
+                    // Work out if we need the value or it's address on the stack
                     bool NeedValueAddress = (memberType.IsValueType && (toStringTypes.Contains(memberType) || otherSupportedTypes.Contains(memberType)));
                     if (Nullable.GetUnderlyingType(memberType) != null)
                     {
@@ -241,18 +251,23 @@ namespace PetaJson
                 // Call IJsonWritten
                 if (typeof(IJsonWritten).IsAssignableFrom(type))
                 {
-                    il.Emit(OpCodes.Ldloc, locTypedObj);
                     if (type.IsValueType)
-                        il.Emit(OpCodes.Box, type);
-                    il.Emit(OpCodes.Castclass, typeof(IJsonWritten));
-                    il.Emit(OpCodes.Ldarg_0);
-                    il.Emit(OpCodes.Callvirt, typeof(IJsonWritten).GetMethod("OnJsonWritten", new Type[] { typeof(IJsonWriter) }));
+                    {
+                        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);
-
-                // Create delegate to our IL code
                 var impl = (Action<IJsonWriter, object>)method.CreateDelegate(typeof(Action<IJsonWriter, object>));
 
                 // Wrap it in a call to WriteDictionary
@@ -266,8 +281,28 @@ namespace PetaJson
 
             }
 
-            public static Action<IJsonReader, object> MakeIntoParser(Type type)
+            // Pseudo box lets us pass a value type by reference.  Used during 
+            // deserialization of value types.
+            interface IPseudoBox
+            {
+                object GetValue();
+            }
+            class PseudoBox<T> : IPseudoBox where T : struct
             {
+                public T value;
+
+                object IPseudoBox.GetValue()
+                {
+                    return value;
+                }
+            }
+
+
+            // Make a parser for value types
+            public static Func<IJsonReader, Type, object> MakeParser(Type type)
+            {
+                System.Diagnostics.Debug.Assert(type.IsValueType);
+
                 // Get the reflection info for this type
                 var ri = ReflectionInfo.GetReflectionInfo(type);
                 if (ri == null)
@@ -276,14 +311,155 @@ namespace PetaJson
                 // We'll create setters for each property/field
                 var setters = new Dictionary<string, Action<IJsonReader, object>>();
 
-                // These types we'll call <type>.Parse(reader.String) on
-                var numericTypes = new Type[] { 
-                    typeof(int), typeof(uint), typeof(long), typeof(ulong), 
-                    typeof(short), typeof(ushort), typeof(decimal), 
-                    typeof(byte), typeof(sbyte), 
-                    typeof(double), typeof(float)
+                // 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() == 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());
+                    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 = Activator.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;
+            }
+
+            // Helper to make the call to a PsuedoBox value's IJsonLoading or IJsonLoaded
+            static Action<object, IJsonReader> MakeInterfaceCall(Type type, Type tItf)
+            {
+                // Interface supported?
+                if (!tItf.IsAssignableFrom(type))
+                    return null;
+
+                // Resolve the box type
+                var boxType = typeof(PseudoBox<>).MakeGenericType(type);
+
+                // Create method
+                var method = new DynamicMethod("dynamic_invoke_" + tItf.Name, null, new Type[] { typeof(object), typeof(IJsonReader) }, true);
+                var il = method.GetILGenerator();
+
+                // Call interface method
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(OpCodes.Castclass, boxType);
+                il.Emit(OpCodes.Ldflda, boxType.GetField("value"));
+                il.Emit(OpCodes.Ldarg_1);
+                il.Emit(OpCodes.Call, type.GetInterfaceMap(tItf).TargetMethods[0]);
+                il.Emit(OpCodes.Ret);
+
+                // Done
+                return (Action<object, IJsonReader>)method.CreateDelegate(typeof(Action<object, IJsonReader>));
+            }
+
+            // Similar to above but for IJsonLoadField
+            static Func<object, IJsonReader, string, bool> MakeLoadFieldCall(Type type)
+            {
+                // Interface supported?
+                var tItf = typeof(IJsonLoadField);
+                if (!tItf.IsAssignableFrom(type))
+                    return null;
+
+                // Resolve the box type
+                var boxType = typeof(PseudoBox<>).MakeGenericType(type);
+
+                // Create method
+                var method = new DynamicMethod("dynamic_invoke_" + tItf.Name, typeof(bool), new Type[] { typeof(object), typeof(IJsonReader), typeof(string) }, true);
+                var il = method.GetILGenerator();
+
+                // Call interface method
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(OpCodes.Castclass, boxType);
+                il.Emit(OpCodes.Ldflda, boxType.GetField("value"));
+                il.Emit(OpCodes.Ldarg_1);
+                il.Emit(OpCodes.Ldarg_2);
+                il.Emit(OpCodes.Call, type.GetInterfaceMap(tItf).TargetMethods[0]);
+                il.Emit(OpCodes.Ret);
+
+                // Done
+                return (Func<object, IJsonReader, string, bool>)method.CreateDelegate(typeof(Func<object, IJsonReader, string, bool>));
+            }
+
+            // Create an "into parser" that can parse from IJsonReader into a reference type (ie: a class)
+            public static Action<IJsonReader, object> MakeIntoParser(Type type)
+            {
+                System.Diagnostics.Debug.Assert(!type.IsValueType);
+
+                // 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>>();
+
                 // Process all members
                 foreach (var m in ri.Members)
                 {
@@ -294,6 +470,8 @@ namespace PetaJson
                     {
                         continue;
                     }
+
+                    // Ignore read only properties that has KeepInstance attribute
                     if (pi != null && pi.GetGetMethod() == null && m.KeepInstance)
                     {
                         continue;
@@ -305,8 +483,9 @@ namespace PetaJson
 
                     // Load the target
                     il.Emit(OpCodes.Ldarg_1);
-                    il.Emit(type.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, type);
+                    il.Emit(OpCodes.Castclass, type);
 
+                    // Try to keep existing instance?
                     if (m.KeepInstance)
                     {
                         // Get existing existing instance
@@ -338,89 +517,42 @@ namespace PetaJson
                         il.MarkLabel(lblExistingInstanceNull);
                     }
 
-                    Action<string> callHelper = helperName =>
-                    {
-                        // check we have a string
-                        il.Emit(OpCodes.Ldarg_0);
-                        il.Emit(OpCodes.Call, typeof(Emit).GetMethod(helperName, new Type[] { typeof(IJsonReader) }));
-
-                        il.Emit(OpCodes.Ldarg_0);
-                        il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
-                    };
-
-                    if (m.MemberType == typeof(string))
-                    {
-                        callHelper("GetLiteralString");
-                    }
-
-                    else if (m.MemberType == typeof(bool))
-                    {
-                        callHelper("GetLiteralBool");
-                    }
-
-                    else if (m.MemberType == typeof(char))
-                    {
-                        callHelper("GetLiteralChar");
-                    }
-
-                    else if (numericTypes.Contains(m.MemberType))
-                    {
-                        il.Emit(OpCodes.Ldarg_0);
-                        il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralNumber", new Type[] { typeof(IJsonReader) }));
-
-                        // Convert to a string
-                        il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
-                        il.Emit(OpCodes.Call, m.MemberType.GetMethod("Parse", new Type[] { typeof(string), typeof(IFormatProvider) }));
-
-                        il.Emit(OpCodes.Ldarg_0);
-                        il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
-                    }
-
-                    else
-                    {
-                        il.Emit(OpCodes.Ldarg_0);
-                        il.Emit(OpCodes.Ldtoken, m.MemberType);
-                        il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
-                        il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("Parse", new Type[] { typeof(Type) }));
-                        il.Emit(m.MemberType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, m.MemberType);
-                    }
+                    // Get the value from IJsonReader
+                    GenerateGetJsonValue(m, il);
 
+                    // Assign it
                     if (pi != null)
-                    {
-                        il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, pi.GetSetMethod());
-                    }
-
+                        il.Emit(OpCodes.Callvirt, pi.GetSetMethod());
                     if (fi != null)
-                    {
                         il.Emit(OpCodes.Stfld, fi);
-                    }
 
+                    // Done
                     il.Emit(OpCodes.Ret);
 
                     // Store the handler in map
                     setters.Add(m.JsonKey, (Action<IJsonReader, object>)method.CreateDelegate(typeof(Action<IJsonReader, object>)));
                 }
 
+
                 // Now create the parseInto delegate
-                bool hasLoading = typeof(IJsonLoading).IsAssignableFrom(type);
-                bool hasLoaded = typeof(IJsonLoaded).IsAssignableFrom(type);
                 Action<IJsonReader, object> parseInto = (reader, obj) =>
                 {
-                    if (hasLoading)
-                    {
-                        ((IJsonLoading)obj).OnJsonLoading(reader);
-                    }
+                    // Call IJsonLoading
+                    var loading = obj as IJsonLoading;
+                    if (loading!=null)
+                        loading.OnJsonLoading(reader);
 
+                    // Cache IJsonLoadField
                     var lf = obj as IJsonLoadField;
 
-                    reader.ReadDictionary(key =>
+                    // Read dictionary keys
+                    reader.ParseDictionary(key =>
                     {
-                        if (lf != null)
-                        {
-                            if (lf.OnJsonField(reader, key))
-                                return;
-                        }
+                        // Call IJsonLoadField
+                        if (lf != null && lf.OnJsonField(reader, key))
+                            return;
 
+                        // Call setters
                         Action<IJsonReader, object> setter;
                         if (setters.TryGetValue(key, out setter))
                         {
@@ -428,40 +560,31 @@ namespace PetaJson
                         }
                     });
 
-                    if (hasLoaded)
-                    {
-                        ((IJsonLoaded)obj).OnJsonLoaded(reader);
-                    }
+                    // Call IJsonLoaded
+                    var loaded = obj as IJsonLoaded;
+                    if (loaded != null)
+                        loaded.OnJsonLoaded(reader);
                 };
 
-                // While we're at it, we might as well create a direct type converter too
-                RegisterParser(type, parseInto);
+                // Since we've created the ParseInto handler, we might as well register
+                // as a Parse handler too.
+                RegisterIntoParser(type, parseInto);
 
                 // Done
                 return parseInto;
             }
 
-            static void RegisterParser(Type type, Action<IJsonReader, object> parseInto)
+            // Registers a ParseInto handler as Parse handler that instantiates the object
+            // and then parses into it.
+            static void RegisterIntoParser(Type type, Action<IJsonReader, object> parseInto)
             {
                 // Create a dynamic method that can do the work
-                var method = new DynamicMethod("dynamic_factory", typeof(object), new Type[] { typeof(IJsonReader), typeof(Action<IJsonReader, object>)}, true);
+                var method = new DynamicMethod("dynamic_factory", typeof(object), new Type[] { typeof(IJsonReader), typeof(Action<IJsonReader, object>) }, true);
                 var il = method.GetILGenerator();
 
                 // Create the new object
                 var locObj = il.DeclareLocal(typeof(object));
-                if (type.IsValueType)
-                {
-                    // Create boxed type
-                    var locTempStruct = il.DeclareLocal(type);
-                    il.Emit(OpCodes.Ldloca, locTempStruct);
-                    il.Emit(OpCodes.Initobj);
-                    il.Emit(OpCodes.Ldloc, locTempStruct);
-                    il.Emit(OpCodes.Box, type);
-                }
-                else
-                {
-                    il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
-                }
+                il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
 
                 il.Emit(OpCodes.Dup);               // For return value
 
@@ -473,7 +596,7 @@ namespace PetaJson
                 il.Emit(OpCodes.Callvirt, typeof(Action<IJsonReader, object>).GetMethod("Invoke"));
                 il.Emit(OpCodes.Ret);
 
-                var factory = (Func<IJsonReader, Action<IJsonReader,object>, object>)method.CreateDelegate(typeof(Func<IJsonReader, Action<IJsonReader, object>, object>));
+                var factory = (Func<IJsonReader, Action<IJsonReader, object>, object>)method.CreateDelegate(typeof(Func<IJsonReader, Action<IJsonReader, object>, object>));
 
                 Json.RegisterParser(type, (reader, type2) =>
                 {
@@ -481,6 +604,68 @@ namespace PetaJson
                 });
             }
 
+            // Generate the MSIL to retrieve a value for a particular field or property from a IJsonReader
+            private static void GenerateGetJsonValue(JsonMemberInfo m, ILGenerator il)
+            {
+                Action<string> generateCallToHelper = helperName =>
+                {
+                    // Call the helper
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Call, typeof(Emit).GetMethod(helperName, new Type[] { typeof(IJsonReader) }));
+
+                    // Move to next token
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                };
+
+                Type[] numericTypes = new Type[] { 
+                    typeof(int), typeof(uint), typeof(long), typeof(ulong), 
+                    typeof(short), typeof(ushort), typeof(decimal), 
+                    typeof(byte), typeof(sbyte), 
+                    typeof(double), typeof(float)
+                };
+
+                if (m.MemberType == typeof(string))
+                {
+                    generateCallToHelper("GetLiteralString");
+                }
+
+                else if (m.MemberType == typeof(bool))
+                {
+                    generateCallToHelper("GetLiteralBool");
+                }
+
+                else if (m.MemberType == typeof(char))
+                {
+                    generateCallToHelper("GetLiteralChar");
+                }
+
+                else if (numericTypes.Contains(m.MemberType))
+                {
+                    // Get raw number string
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralNumber", new Type[] { typeof(IJsonReader) }));
+
+                    // Convert to a string
+                    il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
+                    il.Emit(OpCodes.Call, m.MemberType.GetMethod("Parse", new Type[] { typeof(string), typeof(IFormatProvider) }));
+
+                    // 
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                }
+
+                else
+                {
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Ldtoken, m.MemberType);
+                    il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("Parse", new Type[] { typeof(Type) }));
+                    il.Emit(m.MemberType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, m.MemberType);
+                }
+            }
+
+            // Helper to fetch a literal bool from an IJsonReader
             public static bool GetLiteralBool(IJsonReader r)
             {
                 switch (r.GetLiteralKind())
@@ -496,6 +681,7 @@ namespace PetaJson
                 }
             }
 
+            // Helper to fetch a literal character from an IJsonReader
             public static char GetLiteralChar(IJsonReader r)
             {
                 if (r.GetLiteralKind() != LiteralKind.String)
@@ -507,6 +693,7 @@ namespace PetaJson
                 return str[0];
             }
 
+            // Helper to fetch a literal string from an IJsonReader
             public static string GetLiteralString(IJsonReader r)
             {
                 if (r.GetLiteralKind() != LiteralKind.String)
@@ -514,6 +701,7 @@ namespace PetaJson
                 return r.GetLiteralString();
             }
 
+            // Helper to fetch a literal number from an IJsonReader (returns the raw string)
             public static string GetLiteralNumber(IJsonReader r)
             {
                 switch (r.GetLiteralKind())

+ 0 - 3
TestCases/Program.cs

@@ -42,9 +42,6 @@ namespace TestCases
             object inst = new MyStruct();
 
             fn(inst);
-
-
-            int x = 3;
         }
 
 

+ 1 - 0
TestCases/TestCases.csproj

@@ -55,6 +55,7 @@
     <Compile Include="TestAbstractTypes.cs" />
     <Compile Include="TestCustomFormat.cs" />
     <Compile Include="TestOptions.cs" />
+    <Compile Include="TestsEvents.cs" />
     <Compile Include="TestsGeneral.cs" />
     <Compile Include="TestsReflection.cs" />
   </ItemGroup>

+ 70 - 0
TestCases/TestsEvents.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PetaTest;
+using PetaJson;
+
+namespace TestCases
+{
+    [Json]
+    struct StructEvents : IJsonLoaded, IJsonLoading, IJsonLoadField, IJsonWriting, IJsonWritten
+    {
+        public int IntField;
+
+        [JsonExclude] public bool loading;
+        [JsonExclude] public bool loaded;
+        [JsonExclude] public bool fieldLoaded;
+
+        void IJsonLoaded.OnJsonLoaded(IJsonReader r)
+        {
+            loaded = true;
+        }
+
+        void IJsonLoading.OnJsonLoading(IJsonReader r)
+        {
+            loading = true;
+        }
+
+        bool IJsonLoadField.OnJsonField(IJsonReader r, string key)
+        {
+            fieldLoaded = true;
+            return false;
+        }
+
+        void IJsonWriting.OnJsonWriting(IJsonWriter w)
+        {
+            w.WriteRaw("/* OnJsonWriting */");
+        }
+
+        void IJsonWritten.OnJsonWritten(IJsonWriter w)
+        {
+            w.WriteRaw("/* OnJsonWritten */");
+        }
+    }
+
+
+    [TestFixture]
+    public class TestsEvents
+    {
+        [Test]
+        public void TestStructLoadEvents()
+        {
+            var o2 = Json.Parse<StructEvents>("{\"IntField\":23}");
+            Assert.IsTrue(o2.loading);
+            Assert.IsTrue(o2.loaded);
+            Assert.IsTrue(o2.fieldLoaded);
+        }
+
+        [Test]
+        public void TestStructWriteEvents()
+        {
+            var o = new StructEvents();
+            o.IntField = 23;
+
+            var json = Json.Format(o);
+            Assert.Contains(json, "OnJsonWriting");
+            Assert.Contains(json, "OnJsonWritten");
+        }
+    }
+}

+ 1 - 1
readme.md

@@ -27,7 +27,7 @@ Here goes, a 5 minute whirl-wind tour of using PetaJson...
 2. Optionally add "using PetaJson;" clauses as required
 3. That's it
 
-# Setup (performance)
+## Setup (performance)
 
 1. As above + also add PetaJsonEmit.cs to your project
 2. Call PetaJson.JsonEmit.Init() from your startup code

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác