diff options
Diffstat (limited to 'Assets/ThirdParty/VRM/VRM/UniJSON/Scripts/Json/JsonSchema.cs')
-rw-r--r-- | Assets/ThirdParty/VRM/VRM/UniJSON/Scripts/Json/JsonSchema.cs | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/VRM/UniJSON/Scripts/Json/JsonSchema.cs b/Assets/ThirdParty/VRM/VRM/UniJSON/Scripts/Json/JsonSchema.cs new file mode 100644 index 00000000..6e573467 --- /dev/null +++ b/Assets/ThirdParty/VRM/VRM/UniJSON/Scripts/Json/JsonSchema.cs @@ -0,0 +1,445 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace UniJSON +{ + public class JsonSchema : IEquatable<JsonSchema> + { + public string Schema; // http://json-schema.org/draft-04/schema + + #region Annotations + string m_title; + public string Title + { + get { return m_title; } + private set + { + if (value == null) + { + m_title = ""; + } + else + { + m_title = value.Trim(); + } + } + } + + string m_desc; + public string Description + { + get { return m_desc; } + private set + { + if (value == null) + { + m_desc = ""; + } + else + { + m_desc = value.Trim(); + } + } + } + + public object Default + { + get; + private set; + } + #endregion + + public IJsonSchemaValidator Validator { get; set; } + + /// <summary> + /// Skip validator comparison + /// </summary> + public bool SkipComparison { get; set; } + + public object ExplicitIgnorableValue { private get; set; } + public int ExplicitIgnorableItemLength { private get; set; } + + public override string ToString() + { + return string.Format("<{0}>", Title); + } + + public override int GetHashCode() + { + return 1; + } + + public override bool Equals(object obj) + { + var rhs = obj as JsonSchema; + if (rhs == null) return false; + return Equals(rhs); + } + + public bool Equals(JsonSchema rhs) + { + // skip comparison + if (SkipComparison) return true; + if (rhs.SkipComparison) return true; + return Validator.Equals(rhs.Validator); + } + + public static bool operator ==(JsonSchema obj1, JsonSchema obj2) + { + if (ReferenceEquals(obj1, obj2)) + { + return true; + } + + if (ReferenceEquals(obj1, null)) + { + return false; + } + + if (ReferenceEquals(obj2, null)) + { + return false; + } + + return obj1.Equals(obj2); + } + + public static bool operator !=(JsonSchema obj1, JsonSchema obj2) + { + return !(obj1 == obj2); + } + + #region FromType + public static JsonSchema FromType<T>() + { + return FromType(typeof(T), null, null); + } + + public static JsonSchema FromType(Type t, + BaseJsonSchemaAttribute a = null, // field attribute + ItemJsonSchemaAttribute ia = null + ) + { + // class attribute + var aa = t.GetCustomAttributes(typeof(JsonSchemaAttribute), true) + .FirstOrDefault() as JsonSchemaAttribute; + if (a != null) + { + a.Merge(aa); + } + else + { + if (aa == null) + { + a = new JsonSchemaAttribute(); + } + else + { + a = aa; + } + } + + if (ia == null) + { + ia = t.GetCustomAttributes(typeof(ItemJsonSchemaAttribute), true) + .FirstOrDefault() as ItemJsonSchemaAttribute; + } + + IJsonSchemaValidator validator = null; + bool skipComparison = a.SkipSchemaComparison; + if (t == typeof(object)) + { + skipComparison = true; + } + + if (a.EnumValues != null) + { + try + { + validator = JsonEnumValidator.Create(a.EnumValues, a.EnumSerializationType); + } + catch (Exception) + { + throw new Exception(String.Join(", ", a.EnumValues.Select(x => x.ToString()).ToArray())); + } + } + else if (t.IsEnum) + { + validator = JsonEnumValidator.Create(t, a.EnumSerializationType, a.EnumExcludes); + } + else + { + validator = JsonSchemaValidatorFactory.Create(t, a, ia); + } + + var schema = new JsonSchema + { + Title = a.Title, + Description = a.Description, + Validator = validator, + SkipComparison = skipComparison, + ExplicitIgnorableValue = a.ExplicitIgnorableValue, + ExplicitIgnorableItemLength = a.ExplicitIgnorableItemLength, + }; + + return schema; + } + #endregion + + #region FromJson + static ValueNodeType ParseValueType(string type) + { + try + { + return (ValueNodeType)Enum.Parse(typeof(ValueNodeType), type, true); + } + catch (ArgumentException) + { + throw new ArgumentException(string.Format("unknown type: {0}", type)); + } + } + + Stack<string> m_context = new Stack<string>(); + + static Utf8String s_ref = Utf8String.From("$ref"); + + public void Parse(IFileSystemAccessor fs, ListTreeNode<JsonValue> root, string Key) + { + m_context.Push(Key); + + var compositionType = default(CompositionType); + var composition = new List<JsonSchema>(); + foreach (var kv in root.ObjectItems()) + { + switch (kv.Key.GetString()) + { + case "$schema": + Schema = kv.Value.GetString(); + break; + + case "$ref": + { + var refFs = fs.Get(kv.Value.GetString()); + + // parse JSON + var json = refFs.ReadAllText(); + var refRoot = JsonParser.Parse(json); + + Parse(refFs, refRoot, "$ref"); + } + break; + + #region Annotation + case "title": + Title = kv.Value.GetString(); + break; + + case "description": + Description = kv.Value.GetString(); + break; + + case "default": + Default = kv.Value; + break; + #endregion + + #region Validation + // http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1 + case "type": + if (Validator == null) + { + Validator = JsonSchemaValidatorFactory.Create(kv.Value.GetString()); + } + break; + + case "enum": + Validator = JsonEnumValidator.Create(kv.Value); + break; + + case "const": + break; + #endregion + + #region Composite + // http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.7 + case "oneOf": + break; + + case "not": + break; + + case "anyOf": // composition + case "allOf": // composition + { + compositionType = (CompositionType)Enum.Parse(typeof(CompositionType), kv.Key.GetString(), true); + foreach (var item in kv.Value.ArrayItems()) + { + if (item.ContainsKey(s_ref)) + { + var sub = JsonSchema.ParseFromPath(fs.Get(item[s_ref].GetString())); + composition.Add(sub); + } + else + { + var sub = new JsonSchema(); + sub.Parse(fs, item, compositionType.ToString()); + composition.Add(sub); + } + } + Composite(compositionType, composition); + } + break; + #endregion + + // http://json-schema.org/latest/json-schema-validation.html#rfc.section.7 + case "format": + break; + + #region Gltf + case "gltf_detailedDescription": + break; + + case "gltf_webgl": + break; + + case "gltf_uriType": + break; + #endregion + + default: + { + if (Validator != null) + { + if (Validator.FromJsonSchema(fs, kv.Key.GetString(), kv.Value)) + { + continue; + } + } + throw new NotImplementedException(string.Format("unknown key: {0}", kv.Key)); + } + } + } + m_context.Pop(); + + if (Validator == null) + { + SkipComparison = true; + } + } + + void Composite(CompositionType compositionType, List<JsonSchema> composition) + { + switch (compositionType) + { + case CompositionType.AllOf: + if (composition.Count == 1) + { + // inheritance + if (Validator == null) + { + //Validator = JsonSchemaValidatorFactory.Create(composition[0].Validator.ValueNodeType); + Validator = composition[0].Validator; + } + else + { + Validator.Merge(composition[0].Validator); + } + } + else + { + throw new NotImplementedException(); + } + break; + + case CompositionType.AnyOf: + if (Validator == null) + { + if (composition.Count == 1) + { + throw new NotImplementedException(); + //Validator = composition[0].Validator; + } + else + { + // extend enum + // enum, enum..., type + Validator = JsonEnumValidator.Create(composition, EnumSerializationType.AsString); + } + } + //throw new NotImplementedException(); + break; + + default: + throw new NotImplementedException(); + } + } + + public static JsonSchema ParseFromPath(IFileSystemAccessor fs) + { + // parse JSON + var json = fs.ReadAllText(); + var root = JsonParser.Parse(json); + + // create schema + var schema = new JsonSchema(); + schema.Parse(fs, root, "__ParseFromPath__" + fs.ToString()); + return schema; + } + #endregion + + public void Serialize<T>(IFormatter f, T o, JsonSchemaValidationContext c = null) + { + if (c == null) + { + c = new JsonSchemaValidationContext(o) + { + EnableDiagnosisForNotRequiredFields = true, + }; + } + + var ex = Validator.Validate(c, o); + if (ex != null) + { + throw ex; + } + + Validator.Serialize(f, c, o); + } + + public void ToJson(IFormatter f) + { + f.BeginMap(2); + if (!string.IsNullOrEmpty(Title)) { f.Key("title"); f.Value(Title); } + if (!string.IsNullOrEmpty(Description)) { f.Key("description"); f.Value(Description); } + Validator.ToJsonSchema(f); + f.EndMap(); + } + + public bool IsExplicitlyIgnorableValue<T>(T obj) + { + if (obj == null) + { + 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<T>(this JsonSchema s, T o, JsonSchemaValidationContext c = null) + { + var f = new JsonFormatter(); + s.Serialize(f, o, c); + return f.ToString(); + } + } +} |