From 60041f70c3a1cc159f48be13e4b0dc2d62f8eb54 Mon Sep 17 00:00:00 2001 From: yutopp Date: Tue, 8 Jan 2019 16:51:39 +0900 Subject: [PATCH 1/9] Add a test for JsonSchema.Serialize --- .../Tests/Json/SerializeWithSchemaTests.cs | 38 +++++++++++++++++++ .../Json/SerializeWithSchemaTests.cs.meta | 12 ++++++ 2 files changed, 50 insertions(+) create mode 100644 Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs create mode 100644 Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs.meta diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs new file mode 100644 index 000000000..5d0556954 --- /dev/null +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs @@ -0,0 +1,38 @@ +using NUnit.Framework; + +namespace UniJSON +{ + public class SerializeWithSchemaTests + { + [JsonSchema(Title="CheckConstraintsTest")] + public class CheckConstraintsTest + { + [JsonSchema(Minimum = 0)] + public int X; + + [JsonSchema(Minimum = 10)] // Not required, thus ignored when the value violates the constraints + public int Y; + } + + [Test] + public void TestCheckConstraints() + { + var obj = new CheckConstraintsTest() + { + X = 0, + Y = 0, // Will be excluded because 0 doesn't satisfy a requirement of "Minimum = 10" + }; + + var s = JsonSchema.FromType(); + { + var c = new JsonSchemaValidationContext(obj); + Assert.Null(s.Validator.Validate(c, s)); + } + var actual = s.Serialize(obj); + + var expected = @"{""X"":0}"; + + Assert.AreEqual(expected, actual); + } + } +} diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs.meta b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs.meta new file mode 100644 index 000000000..b0c9e7173 --- /dev/null +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f1d1c3d9d4d20db409e9d5d9d671abbe +timeCreated: 1546930461 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 317699ef41f65f4da11c824bd8605a5d831a93c0 Mon Sep 17 00:00:00 2001 From: yutopp Date: Tue, 8 Jan 2019 18:26:52 +0900 Subject: [PATCH 2/9] Add an option to check all fields by JsonSchema --- .../Editor/Tests/Json/ValidatorTests.cs | 35 +++++++++++++++ .../IJsonSchemaValidator.cs | 2 + .../JsonObjectValidator.cs | 45 +++++++++---------- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs index d6a475251..7165a3d37 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs @@ -199,6 +199,41 @@ namespace UniJSON Assert.True(c.IsEmpty()); } + class NotRequired + { + [JsonSchema(Minimum = 1)] + public int Value; + } + + [Test] + public void ObjectValidatorForNotRequired() + { + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = false, // Default behaviour + }; + + var s = JsonSchema.FromType(); + // An error is not returned because Value is not 'Required' and the diagnosis is not enabled + Assert.Null(s.Validator.Validate(c, new Hoge { Value = 0 })); + + Assert.True(c.IsEmpty()); + } + + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = true, + }; + + var s = JsonSchema.FromType(); + Assert.NotNull(s.Validator.Validate(c, new Hoge { Value = 0 })); + + Assert.True(c.IsEmpty()); + } + } + [Test] public void DictionaryValidator() { diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/IJsonSchemaValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/IJsonSchemaValidator.cs index 6c500e0d3..9a53769d8 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/IJsonSchemaValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/IJsonSchemaValidator.cs @@ -7,6 +7,8 @@ namespace UniJSON { Stack m_stack = new Stack(); + public bool EnableDiagnosisForNotRequiredFields = false; + public JsonSchemaValidationContext(object o) { Push(o.GetType().Name); diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs index ae943ae65..a4dd1e8e6 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs @@ -28,11 +28,11 @@ namespace UniJSON get; set; } - List m_required = new List(); + HashSet m_required = new HashSet(); /// /// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.5.3 /// - public List Required + public HashSet Required { get { return m_required; } } @@ -302,7 +302,7 @@ namespace UniJSON { class ObjectValidator { - delegate JsonSchemaValidationException FieldValidator(IJsonSchemaValidator v, + delegate JsonSchemaValidationException FieldValidator(IJsonSchemaValidator v, JsonSchemaValidationContext c, T o); Dictionary m_validators = new Dictionary(); @@ -341,32 +341,38 @@ namespace UniJSON } } - public JsonSchemaValidationException Validate(List required, Dictionary properties, + public JsonSchemaValidationException Validate( + HashSet required, + Dictionary properties, JsonSchemaValidationContext c, T o) { - foreach (var x in required) + foreach (var kv in properties) { - JsonSchema s; - if(properties.TryGetValue(x, out s)) + var fieldName = kv.Key; + var schema = kv.Value; + + FieldValidator fv; + if (m_validators.TryGetValue(fieldName, out fv)) { - FieldValidator fv; - if (m_validators.TryGetValue(x, out fv)) + var ex = fv(schema.Validator, c, o); + if (ex != null) { - var ex = fv(s.Validator, c, o); - if (ex != null) + if (required.Contains(fieldName) // required fields must be checked + || c.EnableDiagnosisForNotRequiredFields) { return ex; } } } } + return null; } } static ObjectValidator s_validator; - public static JsonSchemaValidationException Validate(List required, + public static JsonSchemaValidationException Validate(HashSet required, Dictionary properties, JsonSchemaValidationContext c, T o) { @@ -390,16 +396,7 @@ namespace UniJSON return new JsonSchemaValidationException(c, "no properties"); } - if (Required != null) - { - var ex = GenericValidator.Validate(Required, Properties, c, o); - if (ex != null) - { - return ex; - } - } - - return null; + return GenericValidator.Validate(Required, Properties, c, o); } static class GenericSerializer @@ -511,7 +508,7 @@ namespace UniJSON GenericSerializer.Serialize(this, f, c, value); } - static class GenericDeserializer + static class GenericDeserializer where S : IListTreeItem, IValue { delegate T Deserializer(ListTreeNode src); @@ -596,7 +593,7 @@ namespace UniJSON } } - public void Deserialize(ListTreeNode src, ref U dst) + public void Deserialize(ListTreeNode src, ref U dst) where T : IListTreeItem, IValue { GenericDeserializer.Deserialize(src, ref dst, Properties); From 77eaa4bcb022d5958526cbf350a9084fceb7049d Mon Sep 17 00:00:00 2001 From: yutopp Date: Tue, 8 Jan 2019 23:28:02 +0900 Subject: [PATCH 3/9] Add/Fix tests for UniJSON. Add ExplicitIgnorableValue option to ignore checks for validations --- .../Editor/Tests/Json/JsonSerializerTests.cs | 3 +- .../UniJSON/Editor/Tests/Json/SchemaTests.cs | 26 ++++++++-- .../Tests/Json/SerializeWithSchemaTests.cs | 43 +++++++++++++++ .../Editor/Tests/Json/ValidatorTests.cs | 52 ++++++++++++++++++- Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs | 15 +++++- .../Scripts/Json/JsonSchemaAttribute.cs | 6 +++ .../JsonObjectValidator.cs | 24 ++++++--- 7 files changed, 152 insertions(+), 17 deletions(-) diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs index 767e55505..0f497e9fb 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/JsonSerializerTests.cs @@ -28,7 +28,7 @@ namespace UniJSON } struct EnumTest - { + { public HogeFuga EnumDefault; [JsonSchema(EnumSerializationType =EnumSerializationType.AsInt)] @@ -104,6 +104,7 @@ namespace UniJSON Assert.AreEqual(1, json.GetObjectCount()); Assert.AreEqual(1, json["Vector"][0].GetInt32()); } + #endregion #region Deserialize diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/SchemaTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/SchemaTests.cs index 66daa11a3..1f51b07a0 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/SchemaTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/SchemaTests.cs @@ -1,7 +1,6 @@ #pragma warning disable 0649 using NUnit.Framework; - namespace UniJSON { public class SchemaTests @@ -40,6 +39,24 @@ namespace UniJSON Assert.AreEqual(0, parsed["properties"]["age"]["minimum"].GetInt32()); } + [JsonSchema(Title="MultipleConstraints")] + public class MultipleConstraints + { + [JsonSchema(Required = true, Minimum = 0, Maximum = 100)] + public int ranged; + } + + [Test] + public void CreateFromClassWithMultipleConstraints() + { + var s = JsonSchema.FromType(); + + var v = s.Validator as JsonObjectValidator; + var rangedV = v.Properties["ranged"].Validator as JsonIntValidator; + Assert.AreEqual(0, rangedV.Minimum); + Assert.AreEqual(100, rangedV.Maximum); + } + public enum ProjectionType { Perspective, @@ -48,7 +65,7 @@ namespace UniJSON class EnumStringTest { - [JsonSchema(EnumSerializationType =EnumSerializationType.AsLowerString)] + [JsonSchema(EnumSerializationType = EnumSerializationType.AsLowerString)] public ProjectionType type; } @@ -81,7 +98,7 @@ namespace UniJSON ] } - + } } "; @@ -117,7 +134,7 @@ namespace UniJSON ] } - + } } "; @@ -129,6 +146,5 @@ namespace UniJSON Assert.AreEqual(fromJson, fromType); } - } } diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs index 5d0556954..e5aacd5cb 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/SerializeWithSchemaTests.cs @@ -34,5 +34,48 @@ namespace UniJSON Assert.AreEqual(expected, actual); } + + [JsonSchema(Title="ObjectNestedTest")] + public class ObjectNestedTest + { + public CheckConstraintsTest C; + } + + [Test] + public void TestObjectNested() + { + var obj = new ObjectNestedTest() + { + C = new CheckConstraintsTest(), + }; + + var s = JsonSchema.FromType(); + { + var c = new JsonSchemaValidationContext(obj); + Assert.Null(s.Validator.Validate(c, s)); + } + var actual = s.Serialize(obj); + + var expected = @"{""C"":{""X"":0}}"; + + Assert.AreEqual(expected, actual); + } + + [Test] + public void TestObjectNestedWithNull() + { + var obj = new ObjectNestedTest(); + + var s = JsonSchema.FromType(); + { + var c = new JsonSchemaValidationContext(obj); + Assert.Null(s.Validator.Validate(c, s)); + } + var actual = s.Serialize(obj); + + var expected = @"{}"; + + Assert.AreEqual(expected, actual); + } } } diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs index 7165a3d37..fc50234b3 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs @@ -216,7 +216,7 @@ namespace UniJSON var s = JsonSchema.FromType(); // An error is not returned because Value is not 'Required' and the diagnosis is not enabled - Assert.Null(s.Validator.Validate(c, new Hoge { Value = 0 })); + Assert.Null(s.Validator.Validate(c, new NotRequired { Value = 0 })); Assert.True(c.IsEmpty()); } @@ -228,7 +228,55 @@ namespace UniJSON }; var s = JsonSchema.FromType(); - Assert.NotNull(s.Validator.Validate(c, new Hoge { Value = 0 })); + Assert.NotNull(s.Validator.Validate(c, new NotRequired { Value = 0 })); + + Assert.True(c.IsEmpty()); + } + } + + class NotRequiredWithIgnorable + { + [JsonSchema(Minimum = 2, ExplicitIgnorableValue = -1)] + public int Value; + } + + [Test] + public void ObjectValidatorForNotRequiredWithIgnorable() + { + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = false, // Default behaviour + }; + + var s = JsonSchema.FromType(); + // An error is not returned because Value is not 'Required' and the diagnosis is not enabled + Assert.Null(s.Validator.Validate(c, new NotRequiredWithIgnorable { Value = 0 })); + + Assert.True(c.IsEmpty()); + } + + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = true, + }; + + var s = JsonSchema.FromType(); + Assert.NotNull(s.Validator.Validate(c, new NotRequiredWithIgnorable { Value = 0 })); + + Assert.True(c.IsEmpty()); + } + + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = true, + }; + + var s = JsonSchema.FromType(); + // An error is NOT returned even though diagnosis is enabled because of an ignorable value is matched + Assert.Null(s.Validator.Validate(c, new NotRequiredWithIgnorable { Value = -1 })); Assert.True(c.IsEmpty()); } diff --git a/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs b/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs index 7adfe12e4..be73d4102 100644 --- a/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs +++ b/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs @@ -57,6 +57,8 @@ namespace UniJSON /// public bool SkipComparison { get; set; } + public object ExplicitIgnorableValue { private get; set; } + public override string ToString() { return string.Format("<{0}>", Title); @@ -175,7 +177,8 @@ namespace UniJSON Title = a.Title, Description = a.Description, Validator = validator, - SkipComparison = skipComparison + SkipComparison = skipComparison, + ExplicitIgnorableValue = a.ExplicitIgnorableValue, }; return schema; @@ -404,6 +407,16 @@ namespace UniJSON Validator.ToJsonScheama(f); f.EndMap(); } + + public bool IsExplicitlyIgnorableValue(T obj) + { + if (obj == null) + { + return ExplicitIgnorableValue == null; + } + + return obj.Equals(ExplicitIgnorableValue); + } } public static class JsonSchemaExtensions diff --git a/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs b/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs index 4171769f1..f7ebad56a 100644 --- a/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs +++ b/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs @@ -55,6 +55,12 @@ namespace UniJSON /// public bool SkipSchemaComparison; + /// + /// Suppress errors if a value of the field which is not required by a schema is matched to this value. + /// This feature will be useful to ignore invalid value which is known. + /// + public object ExplicitIgnorableValue; + public void Merge(BaseJsonSchemaAttribute rhs) { if (rhs == null) return; diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs index a4dd1e8e6..c0b65b0eb 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; - namespace UniJSON { /// @@ -302,18 +301,26 @@ namespace UniJSON { class ObjectValidator { - delegate JsonSchemaValidationException FieldValidator(IJsonSchemaValidator v, - JsonSchemaValidationContext c, T o); + delegate JsonSchemaValidationException FieldValidator( + JsonSchema s, JsonSchemaValidationContext c, T o, bool isRequired); Dictionary m_validators = new Dictionary(); static FieldValidator CreteFieldValidator(Func getter, string name) { - return (v, c, o) => + return (s, c, o, isRequired) => { + var v = s.Validator; using (c.Push(name)) { - return v.Validate(c, getter(o)); + var field = getter(o); + var ex = v.Validate(c, field); + if (ex != null && !isRequired && s.IsExplicitlyIgnorableValue(field)) + { + return null; + } + + return ex; } }; } @@ -354,11 +361,12 @@ namespace UniJSON FieldValidator fv; if (m_validators.TryGetValue(fieldName, out fv)) { - var ex = fv(schema.Validator, c, o); + var isRequired = required.Contains(fieldName); + var ex = fv(schema, c, o, isRequired); if (ex != null) { - if (required.Contains(fieldName) // required fields must be checked - || c.EnableDiagnosisForNotRequiredFields) + if (isRequired // required fields must be checked + || c.EnableDiagnosisForNotRequiredFields) { return ex; } From cbd158c81d66a5df47f1d8e19d92b3d8f9d11d68 Mon Sep 17 00:00:00 2001 From: yutopp Date: Tue, 8 Jan 2019 23:33:59 +0900 Subject: [PATCH 4/9] Add null check --- .../UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs index c0b65b0eb..631ae8563 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonObjectValidator.cs @@ -361,7 +361,7 @@ namespace UniJSON FieldValidator fv; if (m_validators.TryGetValue(fieldName, out fv)) { - var isRequired = required.Contains(fieldName); + var isRequired = required != null && required.Contains(fieldName); var ex = fv(schema, c, o, isRequired); if (ex != null) { From 4d01aff7e37e53d2085d9b1d5cae359d56d2eeee Mon Sep 17 00:00:00 2001 From: yutopp Date: Wed, 9 Jan 2019 22:28:58 +0900 Subject: [PATCH 5/9] Validator checks elements in Array/List. Validator allows Count == 0 --- .../Editor/Tests/Json/ValidatorTests.cs | 50 +++++++++++++++++++ .../JsonSchemaValidator/JsonArrayValidator.cs | 34 +++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs index fc50234b3..b5661c51b 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs @@ -330,5 +330,55 @@ namespace UniJSON Assert.True(c.IsEmpty()); } + + class HasArrayOBject + { + [ItemJsonSchema(Minimum = 0.0, Maximum = 1.0)] + public float[] xs; + } + + [Test] + public void HasArrayObjectValidator() + { + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = true, + }; + + var s = JsonSchema.FromType(); + + Assert.Null(s.Validator.Validate(c, new HasArrayOBject { xs = new float[] {} })); + Assert.Null(s.Validator.Validate(c, new HasArrayOBject { xs = new float[] { 0.5f } })); + Assert.NotNull(s.Validator.Validate(c, new HasArrayOBject { xs = new float[] { 1.5f } })); + + Assert.True(c.IsEmpty()); + } + } + + class HasListObject + { + [ItemJsonSchema(Minimum = 0.0, Maximum = 1.0)] + public List xs; + } + + [Test] + public void HasListObjectValidator() + { + { + var c = new JsonSchemaValidationContext("test") + { + EnableDiagnosisForNotRequiredFields = true, + }; + + var s = JsonSchema.FromType(); + + Assert.Null(s.Validator.Validate(c, new HasListObject { xs = new List {} })); + Assert.Null(s.Validator.Validate(c, new HasListObject { xs = new List { 0.5f } })); + Assert.NotNull(s.Validator.Validate(c, new HasListObject { xs = new List { 1.5f } })); + + Assert.True(c.IsEmpty()); + } + } } } diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs index b162c4354..922d2375f 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -145,10 +146,12 @@ namespace UniJSON } var count = GenericCounter.Count(o); - if (count == 0) + + // Empty array is valid + /*if (count == 0) { return new JsonSchemaValidationException(context, "empty"); - } + }*/ if (MaxItems.HasValue && count > MaxItems.Value) { @@ -160,6 +163,31 @@ namespace UniJSON return new JsonSchemaValidationException(context, "minItems"); } + var v = Items.Validator; + var t = o.GetType(); + IEnumerable iter = null; + if (t.IsArray) + { + iter = o as Array; + } + else if (t.GetIsGenericList()) + { + iter = o as IList; + } + else + { + return new JsonSchemaValidationException(context, "non iterable object"); + } + + foreach(var e in iter) + { + var ex = v.Validate(context, e); + if (ex != null) + { + return ex; + } + }; + return null; } @@ -252,7 +280,7 @@ namespace UniJSON } } - public void Deserialize(ListTreeNode src, ref U dst) + public void Deserialize(ListTreeNode src, ref U dst) where T : IListTreeItem, IValue { src.Deserialize(ref dst); From 12519f26e311dcfc4d1635ef75c0856e6b2219c3 Mon Sep 17 00:00:00 2001 From: yutopp Date: Wed, 9 Jan 2019 22:49:12 +0900 Subject: [PATCH 6/9] Validator allows empty string --- Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs | 6 ++++++ .../Scripts/JsonSchemaValidator/JsonStringValidator.cs | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs index b5661c51b..3a071c619 100644 --- a/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs +++ b/Assets/VRM/UniJSON/Editor/Tests/Json/ValidatorTests.cs @@ -100,6 +100,12 @@ namespace UniJSON { var c = new JsonSchemaValidationContext("test"); + { + var v = new JsonStringValidator(); + Assert.Null(v.Validate(c, "")); + Assert.Null(v.Validate(c, "a")); + } + { var v = new JsonStringValidator(); v.MinLength = 1; diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonStringValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonStringValidator.cs index fb55cedb5..5b5f86d07 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonStringValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonStringValidator.cs @@ -111,10 +111,6 @@ namespace UniJSON } var value = o as string; - if (value.All(x => Char.IsWhiteSpace(x))) - { - return new JsonSchemaValidationException(c, "whitespace"); - } if (MinLength.HasValue && value.Length < MinLength) { From eef02f766556691e195ba66cc0639e8e33cefe36 Mon Sep 17 00:00:00 2001 From: yutopp Date: Wed, 9 Jan 2019 22:52:56 +0900 Subject: [PATCH 7/9] JsonSchema supports ExplicitIgnorableItemLength to ignore length of Array/List. Can pass JsonSchemaValidationContext to Serializer optionally --- Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs | 19 +++++++++++++++---- .../Scripts/Json/JsonSchemaAttribute.cs | 6 ++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs b/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs index be73d4102..1957c4119 100644 --- a/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs +++ b/Assets/VRM/UniJSON/Scripts/Json/JsonSchema.cs @@ -58,6 +58,7 @@ namespace UniJSON public bool SkipComparison { get; set; } public object ExplicitIgnorableValue { private get; set; } + public int ExplicitIgnorableItemLength { private get; set; } public override string ToString() { @@ -179,6 +180,7 @@ namespace UniJSON Validator = validator, SkipComparison = skipComparison, ExplicitIgnorableValue = a.ExplicitIgnorableValue, + ExplicitIgnorableItemLength = a.ExplicitIgnorableItemLength, }; return schema; @@ -386,9 +388,12 @@ namespace UniJSON } #endregion - public void Serialize(IFormatter f, T o) + public void Serialize(IFormatter f, T o, JsonSchemaValidationContext c = null) { - var c = new JsonSchemaValidationContext(o); + if (c == null) + { + c = new JsonSchemaValidationContext(o); + } var ex = Validator.Validate(c, o); if (ex != null) @@ -415,16 +420,22 @@ namespace UniJSON return ExplicitIgnorableValue == null; } + var iter = obj as System.Collections.ICollection; + if (ExplicitIgnorableItemLength != -1 && iter != null) + { + return iter.Count == ExplicitIgnorableItemLength; + } + return obj.Equals(ExplicitIgnorableValue); } } public static class JsonSchemaExtensions { - public static string Serialize(this JsonSchema s, T o) + public static string Serialize(this JsonSchema s, T o, JsonSchemaValidationContext c = null) { var f = new JsonFormatter(); - s.Serialize(f, o); + s.Serialize(f, o, c); return f.ToString(); } } diff --git a/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs b/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs index f7ebad56a..4e44ebea2 100644 --- a/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs +++ b/Assets/VRM/UniJSON/Scripts/Json/JsonSchemaAttribute.cs @@ -61,6 +61,12 @@ namespace UniJSON /// public object ExplicitIgnorableValue; + /// + /// Suppress errors if length of a value of the field which is not required by a schema is matched to this value. + /// This feature will be useful to ignore invalid value which is known. + /// + public int ExplicitIgnorableItemLength = -1; + public void Merge(BaseJsonSchemaAttribute rhs) { if (rhs == null) return; From 32f966ed8c9128327f1abfd3e94044a8bbcb17e3 Mon Sep 17 00:00:00 2001 From: yutopp Date: Wed, 9 Jan 2019 22:56:18 +0900 Subject: [PATCH 8/9] Fix typo. Refactor --- Assets/VRM/UniJSON/Scripts/FormatterExtensionsSerializer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/VRM/UniJSON/Scripts/FormatterExtensionsSerializer.cs b/Assets/VRM/UniJSON/Scripts/FormatterExtensionsSerializer.cs index cced736aa..1273ee582 100644 --- a/Assets/VRM/UniJSON/Scripts/FormatterExtensionsSerializer.cs +++ b/Assets/VRM/UniJSON/Scripts/FormatterExtensionsSerializer.cs @@ -47,7 +47,8 @@ namespace UniJSON } else { - typeof(FormatterExtensionsSerializer).GetMethod("Serialize").MakeGenericMethod(value.GetType()).Invoke(null, new object[] { f, value }); + typeof(FormatterExtensionsSerializer).GetMethod("Serialize") + .MakeGenericMethod(value.GetType()).Invoke(null, new object[] { f, value }); } } @@ -92,7 +93,7 @@ namespace UniJSON var mi = typeof(IFormatter).GetMethod("Value", new Type[] { t }); if (mi != null) { - // premitives + // primitives var self = Expression.Parameter(typeof(IFormatter), "f"); var arg = Expression.Parameter(t, "value"); var call = Expression.Call(self, mi, arg); @@ -164,6 +165,7 @@ namespace UniJSON return (IFormatter f, T value) => schema.Serialize(f, value); } + //throw new NotImplementedException(); } From 579d5331f5fe5e3959d78c013327caed4b69bea7 Mon Sep 17 00:00:00 2001 From: yutopp Date: Wed, 9 Jan 2019 23:02:08 +0900 Subject: [PATCH 9/9] Fix null errors in JsonArrayValidator --- .../Scripts/JsonSchemaValidator/JsonArrayValidator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs index 922d2375f..65a7df587 100644 --- a/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs +++ b/Assets/VRM/UniJSON/Scripts/JsonSchemaValidator/JsonArrayValidator.cs @@ -163,6 +163,11 @@ namespace UniJSON return new JsonSchemaValidationException(context, "minItems"); } + if (Items == null) + { + return null; // There are no json schema for items, success + } + var v = Items.Validator; var t = o.GetType(); IEnumerable iter = null;