Brad Robinson 11 rokov pred
rodič
commit
d3a2e8d47b
6 zmenil súbory, kde vykonal 612 pridanie a 359 odobranie
  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

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 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

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov