holopy3/Assets/UniGLTF/UniJSON/Scripts/Json/JsonSchema.cs
Lena Biresch 490ef558ef CleanUp
2021-01-28 13:07:52 +01:00

400 lines
12 KiB
C#

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 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)
{
validator = JsonEnumValidator.Create(a.EnumValues);
}
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
};
return schema;
}
#endregion
#region FromJson
static JsonValueType ParseValueType(string type)
{
try
{
return (JsonValueType)Enum.Parse(typeof(JsonValueType), type, true);
}
catch (ArgumentException)
{
throw new ArgumentException(string.Format("unknown type: {0}", type));
}
}
Stack<string> m_context = new Stack<string>();
public void Parse(IFileSystemAccessor fs, JsonNode root, string Key)
{
m_context.Push(Key);
var compositionType = default(CompositionType);
var composition = new List<JsonSchema>();
foreach (var kv in root.ObjectItems)
{
//Console.WriteLine(kv.Key);
switch (kv.Key)
{
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.Value.Segment;
break;
#endregion
#region Validation
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1
case "type":
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, true);
foreach (var item in kv.Value.ArrayItems)
{
if (item.ContainsKey("$ref"))
{
var sub = JsonSchema.ParseFromPath(fs.Get(item["$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.Parse(fs, kv.Key, 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.JsonValueType);
Validator = composition[0].Validator;
}
else
{
Validator.Assign(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);
}
}
//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 string Serialize(Object o)
{
var c = new JsonSchemaValidationContext(o);
var ex = Validator.Validate(c, o);
if (ex != null)
{
throw ex;
}
var f = new JsonFormatter();
Validator.Serialize(f, c, o);
return f.ToString();
}
public void ToJson(JsonFormatter f)
{
f.BeginMap();
if (!string.IsNullOrEmpty(Title)) { f.Key("title"); f.Value(Title); }
if (!string.IsNullOrEmpty(Description)) { f.Key("description"); f.Value(Description); }
Validator.ToJson(f);
f.EndMap();
}
}
}