diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs index 50d4aef9f..dc906db9f 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs @@ -1,5 +1,6 @@ #pragma warning disable 0649 using System; +using System.Linq; using NUnit.Framework; using System.Collections.Generic; @@ -138,18 +139,59 @@ namespace UniJSON DeserializeValue(new List { 1 }, "[1]"); //DeserializeValue(new object[] { null, 1, "a" }, "[null,1,\"a\"]"); - DeserializeValue(new Dictionary { }, "{}"); - DeserializeValue(new Dictionary { { "a", 1 } }, "{\"a\":1}"); - DeserializeValue(new Dictionary { { "a", - new Dictionary{ - } } }, "{\"a\":{}}"); - DeserializeValue(new Point { X = 1 }, "{\"X\":1,\"Y\":0}"); DeserializeValue(HogeFuga.Fuga, "1"); DeserializeValue(new EnumTest(), "{\"EnumDefault\":0,\"EnumAsInt\":0,\"EnumAsString\":\"Hoge\",\"EnumAsLowerString\":\"hoge\"}"); } + + class DictionaryValue: IEquatable + { + public Dictionary Dict = new Dictionary(); + + public override bool Equals(object obj) + { + var rhs = obj as DictionaryValue; + if (rhs != null) + { + return Equals(rhs); + } + else + { + return base.Equals(obj); + } + } + + public bool Equals(DictionaryValue other) + { + if(Dict==null && other.Dict == null) + { + return true; + } + else if(Dict==null || other.Dict==null) + { + return false; + } + else + { + return Dict.OrderBy(x => x.Key).SequenceEqual(other.Dict.OrderBy(x => x.Key)); + } + } + } + + [Test] + public void JsonDictionaryDeserializerTest() + { + DeserializeValue(new Dictionary { }, "{}"); + DeserializeValue(new Dictionary { { "a", 1 } }, "{\"a\":1}"); + DeserializeValue(new Dictionary { { "a", + new Dictionary{ + } } }, "{\"a\":{}}"); + + // fix dictionary member deserialization + DeserializeValue(new DictionaryValue(), "{\"Dict\": {}}"); + } #endregion } } diff --git a/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs b/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs new file mode 100644 index 000000000..003044ed6 --- /dev/null +++ b/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs @@ -0,0 +1,53 @@ +using System; +using System.Reflection; + + +namespace UniJSON +{ + struct GenericConstructor + where T : IListTreeItem, IValue + { + static V[] ArrayCreator(ListTreeNode src) + { + if (!src.IsArray()) + { + throw new ArgumentException("value is not array"); + } + var count = src.GetArrayCount(); + return new V[count]; + } + + static Func, U> GetCreator() + { + var t = typeof(U); + if (t.IsArray) + { + var mi = typeof(GenericConstructor).GetMethod("ArrayCreator", + BindingFlags.NonPublic | BindingFlags.Static); + var g = mi.MakeGenericMethod(t.GetElementType()); + return GenericInvokeCallFactory.StaticFunc, U>(g); + } + + { + return _s => + { + return Activator.CreateInstance(); + }; + } + } + + delegate U Creator(ListTreeNode src); + + static Creator s_creator; + + public U Create(ListTreeNode src) + { + if (s_creator == null) + { + var d = GetCreator(); + s_creator = new Creator(d); + } + return s_creator(src); + } + } +} diff --git a/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs.meta b/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs.meta new file mode 100644 index 000000000..dff4825a4 --- /dev/null +++ b/Assets/VRM/UniJSON/Scripts/GenericConstructor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6121925cab5acc54bbb0e0e213783394 +timeCreated: 1549447177 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs b/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs new file mode 100644 index 000000000..b3fd424c9 --- /dev/null +++ b/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + + +namespace UniJSON +{ + public static class GenericDeserializer + where T : IListTreeItem, IValue + { + static V[] GenericArrayDeserializer(ListTreeNode s) + { + if (!s.IsArray()) + { + throw new ArgumentException("not array: " + s.Value.ValueType); + } + var u = new V[s.GetArrayCount()]; + int i = 0; + foreach (var x in s.ArrayItems()) + { + x.Deserialize(ref u[i++]); + } + return u; + } + + static List GenericListDeserializer(ListTreeNode s) + { + if (!s.IsArray()) + { + throw new ArgumentException("not array: " + s.Value.ValueType); + } + var u = new List(s.GetArrayCount()); + foreach (var x in s.ArrayItems()) + { + var e = default(V); + x.Deserialize(ref e); + u.Add(e); + } + return u; + } + + static object DefaultDictionaryDeserializer(ListTreeNode s) + { + switch (s.Value.ValueType) + { + case ValueNodeType.Object: + { + var u = new Dictionary(); + foreach (var kv in s.ObjectItems()) + { + //var e = default(object); + //kv.Value.Deserialize(ref e); + u.Add(kv.Key.GetString(), DefaultDictionaryDeserializer(kv.Value)); + } + return u; + } + + case ValueNodeType.Null: + return null; + + case ValueNodeType.Boolean: + return s.GetBoolean(); + + case ValueNodeType.Integer: + return s.GetInt32(); + + case ValueNodeType.Number: + return s.GetDouble(); + + case ValueNodeType.String: + return s.GetString(); + + default: + throw new NotImplementedException(s.Value.ValueType.ToString()); + } + } + + /// + /// Deserialize Dictionary only string key + /// + /// + /// + /// + static Dictionary DictionaryDeserializer(ListTreeNode s) + { + var d = new Dictionary(); + foreach (var kv in s.ObjectItems()) + { + var value = default(V); + GenericDeserializer.Deserialize(kv.Value, ref value); + d.Add(kv.Key.GetString(), value); + } + return d; + } + + delegate void FieldSetter(ListTreeNode s, object o); + static FieldSetter GetFieldDeserializer(FieldInfo fi) + { + return (s, o) => + { + var u = default(V); + s.Deserialize(ref u); + fi.SetValue(o, u); + }; + } + + static Func, U> GetDeserializer() + { + // primitive + { + var mi = typeof(ListTreeNode).GetMethods().FirstOrDefault(x => + { + if (!x.Name.StartsWith("Get")) + { + return false; + } + + if (!x.Name.EndsWith(typeof(U).Name)) + { + return false; + } + + var parameters = x.GetParameters(); + if (parameters.Length != 0) + { + return false; + } + + if (x.ReturnType != typeof(U)) + { + return false; + } + + return true; + }); + + if (mi != null) + { + return GenericInvokeCallFactory.StaticFunc, U>(mi); + } + } + + var target = typeof(U); + + if (target.IsArray) + { + var mi = typeof(GenericDeserializer).GetMethod("GenericArrayDeserializer", + BindingFlags.Static | BindingFlags.NonPublic); + var g = mi.MakeGenericMethod(target.GetElementType()); + return GenericInvokeCallFactory.StaticFunc, U>(g); + } + + if (target.IsGenericType) + { + if (target.GetGenericTypeDefinition() == typeof(List<>)) + { + var mi = typeof(GenericDeserializer).GetMethod("GenericListDeserializer", + BindingFlags.Static | BindingFlags.NonPublic); + var g = mi.MakeGenericMethod(target.GetGenericArguments()); + return GenericInvokeCallFactory.StaticFunc, U>(g); + } + + if (target == typeof(Dictionary)) + { + + var mi = typeof(GenericDeserializer).GetMethod("DefaultDictionaryDeserializer", + BindingFlags.Static | BindingFlags.NonPublic); + return GenericInvokeCallFactory.StaticFunc, U>(mi); + } + else + if (target.GetGenericTypeDefinition() == typeof(Dictionary<,>) && + target.GetGenericArguments()[0] == typeof(string)) + { + var mi = typeof(GenericDeserializer).GetMethod("DictionaryDeserializer", + BindingFlags.Static | BindingFlags.NonPublic); + var g = mi.MakeGenericMethod(target.GetGenericArguments()[1]); + return GenericInvokeCallFactory.StaticFunc, U>(g); + } + } + + { + var schema = JsonSchema.FromType(); + return s => + { + var t = default(U); + schema.Validator.Deserialize(s, ref t); + return t; + }; + } + +#if false + if (target.IsEnum) + { + var value = Expression.Parameter(typeof(int), "value"); + var cast = Expression.Convert(value, target); + var func = Expression.Lambda(cast, value); + var compiled = (Func)func.Compile(); + return s => + { + return compiled(s.GetInt32()); + }; + } + + { + var fields = target.GetFields(BindingFlags.Instance | BindingFlags.Public); + var fieldDeserializers = fields.ToDictionary(x => Utf8String.From(x.Name), x => + { + var mi = typeof(GenericDeserializer).GetMethod("GetFieldDeserializer", + BindingFlags.Static|BindingFlags.NonPublic); + var g = mi.MakeGenericMethod(x.FieldType); + return (FieldSetter)g.Invoke(null, new object[] { x }); + }); + + return (S s) => + { + if (!s.IsMap()) + { + throw new ArgumentException(s.ValueType.ToString()); + } + + var t = (object)default(GenericCreator).Create(s); + foreach(var kv in s.ObjectItems()) + { + FieldSetter setter; + if (fieldDeserializers.TryGetValue(kv.Key, out setter)) + { + setter(kv.Value, t); + } + } + return (T)t; + }; + } +#endif + } + + public delegate U Deserializer(ListTreeNode node); + + public static Deserializer s_deserializer; + + public static void Deserialize(ListTreeNode node, ref U value) + { + if (s_deserializer == null) + { + var d = GetDeserializer(); + s_deserializer = new Deserializer(d); + } + value = s_deserializer(node); + } + + public static void SetCustomDeserializer(Deserializer deserializer) + { + s_deserializer = deserializer; + } + } +} diff --git a/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs.meta b/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs.meta new file mode 100644 index 000000000..2efc295d6 --- /dev/null +++ b/Assets/VRM/UniJSON/Scripts/GenericDeserializer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 74625bf2a3d6bc64d9d92b08221a8a3d +timeCreated: 1549450914 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonDictionaryValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonDictionaryValidator.cs index 6b97c1e0c..cf191ebf7 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonDictionaryValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonDictionaryValidator.cs @@ -276,7 +276,7 @@ namespace UniJSON public void Deserialize(ListTreeNode src, ref V dst) where U : IListTreeItem, IValue { - throw new NotImplementedException(); + src.Deserialize(ref dst); } } diff --git a/Assets/VRM/UniJSON/Scripts/ListTreeNode/ListTreeNodeDeserializerExtensions.cs b/Assets/VRM/UniJSON/Scripts/ListTreeNode/ListTreeNodeDeserializerExtensions.cs index b41eab5a0..e07560648 100644 --- a/Assets/VRM/UniJSON/Scripts/ListTreeNode/ListTreeNodeDeserializerExtensions.cs +++ b/Assets/VRM/UniJSON/Scripts/ListTreeNode/ListTreeNodeDeserializerExtensions.cs @@ -8,280 +8,10 @@ namespace UniJSON { public static class ListTreeNodeDeserializerExtensions { - struct GenericCreator - where T : IListTreeItem, IValue - { - static V[] ArrayCreator(ListTreeNode src) - { - if (!src.IsArray()) - { - throw new ArgumentException("value is not array"); - } - var count = src.GetArrayCount(); - return new V[count]; - } - - static Func, U> GetCreator() - { - var t = typeof(U); - if (t.IsArray) - { - var mi = typeof(GenericCreator).GetMethod("ArrayCreator", - BindingFlags.NonPublic | BindingFlags.Static); - var g = mi.MakeGenericMethod(t.GetElementType()); - return GenericInvokeCallFactory.StaticFunc, U>(g); - } - - { - return _s => - { - return Activator.CreateInstance(); - }; - } - } - - delegate U Creator(ListTreeNode src); - - static Creator s_creator; - - public U Create(ListTreeNode src) - { - if (s_creator == null) - { - var d = GetCreator(); - s_creator = new Creator(d); - } - return s_creator(src); - } - } - - static object DictionaryDeserializer(ListTreeNode s) - where T : IListTreeItem, IValue - { - switch (s.Value.ValueType) - { - case ValueNodeType.Object: - { - var u = new Dictionary(); - foreach (var kv in s.ObjectItems()) - { - //var e = default(object); - //kv.Value.Deserialize(ref e); - u.Add(kv.Key.GetString(), DictionaryDeserializer(kv.Value)); - } - return u; - } - - case ValueNodeType.Null: - return null; - - case ValueNodeType.Boolean: - return s.GetBoolean(); - - case ValueNodeType.Integer: - return s.GetInt32(); - - case ValueNodeType.Number: - return s.GetDouble(); - - case ValueNodeType.String: - return s.GetString(); - - default: - throw new NotImplementedException(s.Value.ValueType.ToString()); - } - } - public static void Deserialize(this ListTreeNode self, ref U value) where T : IListTreeItem, IValue { GenericDeserializer.Deserialize(self, ref value); } } - - public static class GenericDeserializer - where T : IListTreeItem, IValue - { - static V[] GenericArrayDeserializer(ListTreeNode s) - { - if (!s.IsArray()) - { - throw new ArgumentException("not array: " + s.Value.ValueType); - } - var u = new V[s.GetArrayCount()]; - int i = 0; - foreach (var x in s.ArrayItems()) - { - x.Deserialize(ref u[i++]); - } - return u; - } - - static List GenericListDeserializer(ListTreeNode s) - { - if (!s.IsArray()) - { - throw new ArgumentException("not array: " + s.Value.ValueType); - } - var u = new List(s.GetArrayCount()); - foreach (var x in s.ArrayItems()) - { - var e = default(V); - x.Deserialize(ref e); - u.Add(e); - } - return u; - } - - delegate void FieldSetter(ListTreeNode s, object o); - static FieldSetter GetFieldDeserializer(FieldInfo fi) - { - return (s, o) => - { - var u = default(V); - s.Deserialize(ref u); - fi.SetValue(o, u); - }; - } - - static Func, U> GetDeserializer() - { - // primitive - { - var mi = typeof(ListTreeNode).GetMethods().FirstOrDefault(x => - { - if (!x.Name.StartsWith("Get")) - { - return false; - } - - if (!x.Name.EndsWith(typeof(U).Name)) - { - return false; - } - - var parameters = x.GetParameters(); - if (parameters.Length != 0) - { - return false; - } - - if (x.ReturnType != typeof(U)) - { - return false; - } - - return true; - }); - - if (mi != null) - { - return GenericInvokeCallFactory.StaticFunc, U>(mi); - } - } - - var target = typeof(U); - - if (target.IsArray) - { - var mi = typeof(GenericDeserializer).GetMethod("GenericArrayDeserializer", - BindingFlags.Static | BindingFlags.NonPublic); - var g = mi.MakeGenericMethod(target.GetElementType()); - return GenericInvokeCallFactory.StaticFunc, U>(g); - } - - if (target.IsGenericType) - { - if (target.GetGenericTypeDefinition() == typeof(List<>)) - { - var mi = typeof(GenericDeserializer).GetMethod("GenericListDeserializer", - BindingFlags.Static | BindingFlags.NonPublic); - var g = mi.MakeGenericMethod(target.GetGenericArguments()); - return GenericInvokeCallFactory.StaticFunc, U>(g); - } - - if (target.GetGenericTypeDefinition() == typeof(Dictionary<,>) && - target.GetGenericArguments()[0] == typeof(string)) - { - var mi = typeof(ListTreeNodeDeserializerExtensions).GetMethod("DictionaryDeserializer", - BindingFlags.Static | BindingFlags.NonPublic); - var g = mi.MakeGenericMethod(typeof(T)); - return GenericInvokeCallFactory.StaticFunc, U>(g); - } - } - - { - var schema = JsonSchema.FromType(); - return s => - { - var t = default(U); - schema.Validator.Deserialize(s, ref t); - return t; - }; - } - -#if false - if (target.IsEnum) - { - var value = Expression.Parameter(typeof(int), "value"); - var cast = Expression.Convert(value, target); - var func = Expression.Lambda(cast, value); - var compiled = (Func)func.Compile(); - return s => - { - return compiled(s.GetInt32()); - }; - } - - { - var fields = target.GetFields(BindingFlags.Instance | BindingFlags.Public); - var fieldDeserializers = fields.ToDictionary(x => Utf8String.From(x.Name), x => - { - var mi = typeof(GenericDeserializer).GetMethod("GetFieldDeserializer", - BindingFlags.Static|BindingFlags.NonPublic); - var g = mi.MakeGenericMethod(x.FieldType); - return (FieldSetter)g.Invoke(null, new object[] { x }); - }); - - return (S s) => - { - if (!s.IsMap()) - { - throw new ArgumentException(s.ValueType.ToString()); - } - - var t = (object)default(GenericCreator).Create(s); - foreach(var kv in s.ObjectItems()) - { - FieldSetter setter; - if (fieldDeserializers.TryGetValue(kv.Key, out setter)) - { - setter(kv.Value, t); - } - } - return (T)t; - }; - } -#endif - } - - public delegate U Deserializer(ListTreeNode node); - - public static Deserializer s_deserializer; - - public static void Deserialize(ListTreeNode node, ref U value) - { - if (s_deserializer == null) - { - var d = GetDeserializer(); - s_deserializer = new Deserializer(d); - } - value = s_deserializer(node); - } - - public static void SetCustomDeserializer(Deserializer deserializer) - { - s_deserializer = deserializer; - } - } } diff --git a/Assets/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs b/Assets/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs index 67468f316..eb3699b8e 100644 --- a/Assets/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs +++ b/Assets/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs @@ -219,10 +219,32 @@ namespace VRM [Test] public void MaterialTest() { - var model = new glTF_VRM_Material(); + var model = new glTF_VRM_Material + { + floatProperties = new Dictionary + { + {"float", 1.0f} + }, + vectorProperties = new Dictionary + { + {"vector", new float[]{0, 1, 2, 3 }} + }, + textureProperties = new Dictionary + { + {"texture", 0} + }, + keywordMap = new Dictionary + { + {"keyword", true} + }, + tagMap = new Dictionary + { + {"tag", "map"} + }, + }; var json = model.ToJson(); - Assert.AreEqual(@"{""renderQueue"":-1,""floatProperties"":{},""vectorProperties"":{},""textureProperties"":{},""keywordMap"":{},""tagMap"":{}}", json); + Assert.AreEqual(@"{""renderQueue"":-1,""floatProperties"":{""float"":1},""vectorProperties"":{""vector"":[0,1,2,3]},""textureProperties"":{""texture"":0},""keywordMap"":{""keyword"":true},""tagMap"":{""tag"":""map""}}", json); Debug.Log(json); var c = new JsonSchemaValidationContext("") @@ -231,7 +253,12 @@ namespace VRM }; var json2 = JsonSchema.FromType().Serialize(model, c); // NOTE: New serializer outputs values which will not be used... - Assert.AreEqual(json,json2); + Assert.AreEqual(json, json2); + + // deserialize + var deserialized = default(glTF_VRM_Material); + json.ParseAsJson().Deserialize(ref deserialized); + Assert.AreEqual(1, deserialized.floatProperties.Count); } [Test]