using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using VJson.Internal; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("yutopp")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2019 -")] [assembly: AssemblyDescription("A JSON serializer/deserializer library written in pure C#.")] [assembly: AssemblyFileVersion("0.0.0.0")] [assembly: AssemblyInformationalVersion("0.0.0")] [assembly: AssemblyProduct("VJson")] [assembly: AssemblyTitle("VJson")] [assembly: AssemblyVersion("0.0.0.0")] namespace VJson { public enum EnumConversionType { AsInt, AsString } public class PreserveAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] public sealed class JsonAttribute : PreserveAttribute { public bool ImplicitConstructable; public EnumConversionType EnumConversion; } [AttributeUsage(AttributeTargets.Field)] public sealed class JsonFieldAttribute : PreserveAttribute { public string Name; public int Order; public Type[] TypeHints; public static string FieldName(JsonFieldAttribute f, FieldInfo fi) { if (f != null && !string.IsNullOrEmpty(f.Name)) { return f.Name; } return fi.Name; } public static int FieldOrder(JsonFieldAttribute f) { return f?.Order ?? 0; } } [AttributeUsage(AttributeTargets.Field)] public sealed class JsonFieldIgnorableAttribute : PreserveAttribute { public object WhenValueIs; public int WhenLengthIs; public static bool IsIgnorable(JsonFieldIgnorableAttribute f, T o) { if (f == null) { return false; } if (object.Equals(o, f.WhenValueIs)) { return true; } if ((object)o is Array array) { return array.Length == f.WhenLengthIs; } if ((object)o is IList list) { return list.Count == f.WhenLengthIs; } return false; } } public interface IValidator { } public sealed class JsonDeserializer { private Type _expectedInitialType; public JsonDeserializer(Type type) { _expectedInitialType = type; } public object Deserialize(string text) { using MemoryStream s = new MemoryStream(Encoding.UTF8.GetBytes(text)); return Deserialize(s); } public object Deserialize(Stream s) { using JsonReader jsonReader = new JsonReader(s); INode node = jsonReader.Read(); return DeserializeFromNode(node); } public object DeserializeFromNode(INode node) { return DeserializeValue(node, _expectedInitialType, default(State)); } private object DeserializeValue(INode node, Type expectedType, State state) { NodeKindWrapped targetKind = Node.KindOfTypeWrapped(expectedType); if (targetKind.Wrapped) { expectedType = Node.ValueTypeOfKind(targetKind.Kind); } return DeserializeValueAs(node, targetKind, expectedType, state); } private object DeserializeValueAs(INode node, NodeKindWrapped targetKind, Type targetType, State state) { switch (targetKind.Kind) { case NodeKind.Boolean: return DeserializeToBoolean(node, targetKind, targetType, state); case NodeKind.Integer: case NodeKind.Float: return DeserializeToNumber(node, targetKind, targetType, state); case NodeKind.String: return DeserializeToString(node, targetKind, targetType, state); case NodeKind.Array: return DeserializeToArray(node, targetKind, targetType, state); case NodeKind.Object: return DeserializeToObject(node, targetKind, targetType, state); case NodeKind.Null: return DeserializeToNull(node, targetKind, targetType, state); default: throw new NotImplementedException("Unmatched kind: " + targetKind.Kind); } } private object DeserializeToBoolean(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (node is NullNode) { if (!TypeHelper.IsBoxed(targetType)) { throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } if (!targetKind.Wrapped) { return null; } return NullNode.Null; } if (node is BooleanNode booleanNode) { if (!targetKind.Wrapped) { return CreateInstanceIfConstrucutable(targetType, booleanNode.Value, state); } return node; } throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } private object DeserializeToNumber(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (node is NullNode) { if (!TypeHelper.IsBoxed(targetType)) { throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } if (!targetKind.Wrapped) { return null; } return NullNode.Null; } if (node is IntegerNode integerNode) { if (!targetKind.Wrapped) { return CreateInstanceIfConstrucutable(targetType, integerNode.Value, state); } return node; } if (node is FloatNode floatNode) { if (!targetKind.Wrapped) { return CreateInstanceIfConstrucutable(targetType, floatNode.Value, state); } return node; } throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } private object DeserializeToString(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (node is NullNode) { if (!TypeHelper.IsBoxed(targetType)) { throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } if (!targetKind.Wrapped) { return null; } return NullNode.Null; } if (node is StringNode stringNode) { if (!targetKind.Wrapped) { return CreateInstanceIfConstrucutable(targetType, stringNode.Value, state); } return node; } throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } private object DeserializeToArray(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (!(targetType == typeof(object)) && !targetType.IsArray && (!TypeHelper.TypeWrap(targetType).IsGenericType || !(targetType.GetGenericTypeDefinition() == typeof(List<>)))) { throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } if (node is NullNode) { if (!targetKind.Wrapped) { return null; } return NullNode.Null; } if (node is ArrayNode arrayNode) { if (targetType.IsArray || targetType == typeof(object)) { Type type = targetType; if (type == typeof(object)) { type = typeof(object[]); } int num = ((arrayNode.Elems != null) ? arrayNode.Elems.Count : 0); Array array = (Array)Activator.CreateInstance(type, num); Type elementType = type.GetElementType(); for (int i = 0; i < num; i++) { object value = DeserializeValue(arrayNode.Elems[i], elementType, state.NestAsElem(i)); array.SetValue(value, i); } return array; } int num2 = ((arrayNode.Elems != null) ? arrayNode.Elems.Count : 0); IList list = (IList)Activator.CreateInstance(targetType); Type expectedType = TypeHelper.TypeWrap(targetType).GetGenericArguments()[0]; for (int j = 0; j < num2; j++) { object value2 = DeserializeValue(arrayNode.Elems[j], expectedType, state.NestAsElem(j)); list.Add(value2); } if (targetKind.Wrapped) { return new ArrayNode(list as List); } return list; } throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } private object DeserializeToObject(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (targetKind.Kind != 0) { throw new DeserializeFailureException(state.CreateNodeConversionFailureMessage(node, targetType)); } if (node is NullNode) { if (!targetKind.Wrapped) { return null; } return NullNode.Null; } if (node is ObjectNode objectNode) { if (targetType == typeof(object) || (TypeHelper.TypeWrap(targetType).IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<, >))) { Type type = targetType; if (type == typeof(object)) { type = (targetKind.Wrapped ? typeof(Dictionary) : typeof(Dictionary)); } if (TypeHelper.TypeWrap(type).GetGenericArguments()[0] != typeof(string)) { throw new NotImplementedException(); } IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); if (objectNode.Elems != null) { Type type2 = TypeHelper.TypeWrap(type).GetGenericArguments()[1]; foreach (KeyValuePair elem in objectNode.Elems) { Type expectedType = type2; object value = DeserializeValue(elem.Value, expectedType, state.NestAsElem(elem.Key)); dictionary.Add(elem.Key, value); } } if (targetKind.Wrapped) { return new ObjectNode(dictionary as Dictionary); } return dictionary; } object obj = Activator.CreateInstance(targetType); { foreach (FieldInfo serializableField in TypeHelper.GetSerializableFields(targetType)) { JsonFieldAttribute customAttribute = TypeHelper.GetCustomAttribute(serializableField); string text = JsonFieldAttribute.FieldName(customAttribute, serializableField); INode value2 = null; if (objectNode.Elems == null || !objectNode.Elems.TryGetValue(text, out value2)) { continue; } State state2 = state.NestAsElem(text); if (customAttribute != null && customAttribute.TypeHints != null) { bool flag = false; Type[] typeHints = customAttribute.TypeHints; foreach (Type expectedType2 in typeHints) { try { object value3 = DeserializeValue(value2, expectedType2, state2); serializableField.SetValue(obj, value3); flag = true; } catch (Exception) { continue; } break; } if (!flag) { throw new DeserializeFailureException(state2.CreateNodeConversionFailureMessage(value2, customAttribute.TypeHints)); } } else { Type fieldType = serializableField.FieldType; object value4 = DeserializeValue(value2, fieldType, state2); serializableField.SetValue(obj, value4); } } return obj; } } NodeKindWrapped nodeKindWrapped = default(NodeKindWrapped); nodeKindWrapped.Kind = node.Kind; nodeKindWrapped.Wrapped = targetKind.Wrapped; NodeKindWrapped targetKind2 = nodeKindWrapped; if (targetKind.Wrapped) { targetType = Node.ValueTypeOfKind(node.Kind); } return DeserializeValueAs(node, targetKind2, targetType, state); } private object DeserializeToNull(INode node, NodeKindWrapped targetKind, Type targetType, State state) { if (node is NullNode) { if (!targetKind.Wrapped) { return null; } return NullNode.Null; } throw new NotImplementedException(); } private static object CreateInstanceIfConstrucutable(Type targetType, T value, State state) { if (targetType == typeof(object)) { return value; } Type underlyingType = Nullable.GetUnderlyingType(targetType); if (underlyingType != null) { targetType = underlyingType; } if (TypeHelper.GetConverter(typeof(T), targetType, out var converter)) { if (converter == null) { return value; } if (converter(value, out var output)) { return output; } throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType)); } if (TypeHelper.TypeWrap(targetType).IsEnum) { switch (TypeHelper.GetCustomAttribute(targetType)?.EnumConversion ?? EnumConversionType.AsInt) { case EnumConversionType.AsInt: { Type underlyingType2 = Enum.GetUnderlyingType(targetType); if (!TypeHelper.GetConverter(typeof(T), underlyingType2, out var converter2)) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType)); } object output2; if (converter2 != null) { if (!converter2(value, out output2)) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType)); } } else { output2 = value; } if (!Enum.IsDefined(targetType, output2)) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType, "Enum value is not defined")); } return Enum.ToObject(targetType, output2); } case EnumConversionType.AsString: { int num = Array.IndexOf(TypeHelper.GetStringEnumNames(targetType), value); if (num == -1) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType, "Enum name is not defined")); } return Enum.GetValues(targetType).GetValue(num); } } } JsonAttribute customAttribute = TypeHelper.GetCustomAttribute(targetType); if (customAttribute == null || !customAttribute.ImplicitConstructable) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType, "Not implicit constructable")); } ConstructorInfo? constructor = TypeHelper.TypeWrap(targetType).GetConstructor(new Type[1] { typeof(T) }); if (constructor == null) { throw new DeserializeFailureException(state.CreateTypeConversionFailureMessage(value, targetType, "Suitable constructers are not found")); } return constructor.Invoke(new object[1] { value }); } } public class DeserializeFailureException : Exception { public DeserializeFailureException(string message) : base(message) { } public DeserializeFailureException(string message, DeserializeFailureException inner) : base($"{message}.{inner.Message}") { } } public sealed class JsonReader : IDisposable { private class ReaderWrapper : IDisposable { private StreamReader _reader; private int _lastToken; public ulong Position { get; private set; } public string LastToken { get { if (_lastToken == -1) { return ""; } return ((char)_lastToken).ToString(); } } public ReaderWrapper(Stream s) { _reader = new StreamReader(s); Position = 0uL; } public void Dispose() { if (_reader != null) { ((IDisposable)_reader).Dispose(); } } public int Peek() { _lastToken = _reader.Peek(); return _lastToken; } public int Read() { ulong position = Position + 1; Position = position; _lastToken = _reader.Read(); return _lastToken; } } private ReaderWrapper _reader; private StringBuilder _strCache = new StringBuilder(); public JsonReader(Stream s) { _reader = new ReaderWrapper(s); } public void Dispose() { if (_reader != null) { ((IDisposable)_reader).Dispose(); } } public INode Read() { INode result = ReadElement(); if (_reader.Peek() != -1) { throw NodeExpectedError("EOS"); } return result; } private INode ReadElement() { SkipWS(); INode result = ReadValue(); SkipWS(); return result; } private INode ReadValue() { INode node = null; if ((node = ReadObject()) != null) { return node; } if ((node = ReadArray()) != null) { return node; } if ((node = ReadString()) != null) { return node; } if ((node = ReadNumber()) != null) { return node; } if ((node = ReadLiteral()) != null) { return node; } throw NodeExpectedError("value"); } private INode ReadObject() { int num = _reader.Peek(); if (num != 123) { return null; } _reader.Read(); ObjectNode objectNode = new ObjectNode(); int num2 = 0; while (true) { SkipWS(); num = _reader.Peek(); if (num == 125) { _reader.Read(); return objectNode; } if (num2 > 0) { if (num != 44) { throw TokenExpectedError(','); } _reader.Read(); } SkipWS(); INode node = ReadString(); if (node == null) { throw NodeExpectedError("string"); } SkipWS(); num = _reader.Peek(); if (num != 58) { throw TokenExpectedError(':'); } _reader.Read(); INode node2 = ReadElement(); if (node2 == null) { break; } objectNode.AddElement(((StringNode)node).Value, node2); num2++; } throw NodeExpectedError("element"); } private INode ReadArray() { int num = _reader.Peek(); if (num != 91) { return null; } _reader.Read(); ArrayNode arrayNode = new ArrayNode(); int num2 = 0; while (true) { SkipWS(); num = _reader.Peek(); if (num == 93) { _reader.Read(); return arrayNode; } if (num2 > 0) { if (num != 44) { throw TokenExpectedError(','); } _reader.Read(); } INode node = ReadElement(); if (node == null) { break; } arrayNode.AddElement(node); num2++; } throw NodeExpectedError("element"); } private INode ReadString() { int num = _reader.Peek(); if (num != 34) { return null; } _reader.Read(); while (true) { num = _reader.Peek(); switch (num) { case 34: _reader.Read(); return new StringNode(Regex.Unescape(CommitBuffer())); case 92: _reader.Read(); if (!ReadEscape()) { throw NodeExpectedError("escape"); } continue; } int num2 = _reader.Read(); int num3 = num2; bool num4 = char.IsHighSurrogate((char)num2); if (num4) { num = _reader.Read(); if (!char.IsLowSurrogate((char)num)) { throw NodeExpectedError("low-surrogate"); } num3 = char.ConvertToUtf32((char)num2, (char)num); } if (num3 < 32 || num3 > 1114111) { throw NodeExpectedError("unicode char (0x20 <= char <= 0x10ffff"); } SaveToBuffer(num2); if (num4) { SaveToBuffer(num); } } } private bool ReadEscape() { switch (_reader.Peek()) { case 34: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 92: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 47: SaveToBuffer(_reader.Read()); return true; case 98: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 110: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 114: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 116: SaveToBuffer(92); SaveToBuffer(_reader.Read()); return true; case 117: { SaveToBuffer(92); SaveToBuffer(_reader.Read()); for (int i = 0; i < 4; i++) { if (!ReadHex()) { throw NodeExpectedError("hex"); } } return true; } default: return false; } } private bool ReadHex() { if (ReadDigit()) { return true; } int num = _reader.Peek(); if (num >= 65 && num <= 70) { SaveToBuffer(_reader.Read()); return true; } if (num >= 97 && num <= 102) { SaveToBuffer(_reader.Read()); return true; } return false; } private INode ReadNumber() { if (!ReadInt()) { return null; } int num = 0 | (ReadFrac() ? 1 : 0) | (ReadExp() ? 1 : 0); string s = CommitBuffer(); if (num != 0) { return new FloatNode(double.Parse(s)); } return new IntegerNode(long.Parse(s)); } private bool ReadInt() { if (ReadOneNine()) { ReadDigits(); return true; } if (ReadDigit()) { return true; } if (_reader.Peek() != 45) { return false; } SaveToBuffer(_reader.Read()); if (ReadOneNine()) { ReadDigits(); return true; } if (ReadDigit()) { return true; } throw NodeExpectedError("number"); } private bool ReadDigits() { if (!ReadDigit()) { return false; } while (ReadDigit()) { } return true; } private bool ReadDigit() { if (_reader.Peek() != 48) { return ReadOneNine(); } SaveToBuffer(_reader.Read()); return true; } private bool ReadOneNine() { int num = _reader.Peek(); if (num < 49 || num > 57) { return false; } SaveToBuffer(_reader.Read()); return true; } private bool ReadFrac() { if (_reader.Peek() != 46) { return false; } SaveToBuffer(_reader.Read()); if (!ReadDigits()) { throw NodeExpectedError("digits"); } return true; } private bool ReadExp() { int num = _reader.Peek(); if (num != 69 && num != 101) { return false; } SaveToBuffer(_reader.Read()); ReadSign(); if (!ReadDigits()) { throw NodeExpectedError("digits"); } return true; } private bool ReadSign() { int num = _reader.Peek(); if (num != 43 && num != 45) { return false; } SaveToBuffer(_reader.Read()); return true; } private INode ReadLiteral() { _ = string.Empty; switch (_reader.Peek()) { case 116: if (ConsumeChars(4).ToLower() != "true") { throw NodeExpectedError("true"); } return new BooleanNode(v: true); case 102: if (ConsumeChars(5).ToLower() != "false") { throw NodeExpectedError("false"); } return new BooleanNode(v: false); case 110: if (ConsumeChars(4).ToLower() != "null") { throw NodeExpectedError("null"); } return new NullNode(); default: return null; } } private void SkipWS() { while (true) { int num = _reader.Peek(); if ((uint)(num - 9) <= 1u || num == 13 || num == 32) { _reader.Read(); continue; } break; } } private void SaveToBuffer(int c) { _strCache.Append((char)c); } private string CommitBuffer() { string result = _strCache.ToString(); _strCache.Length = 0; return result; } private string ConsumeChars(int length) { for (int i = 0; i < length; i++) { int c = _reader.Read(); SaveToBuffer(c); } return CommitBuffer(); } private ParseFailedException NodeExpectedError(string expected) { return new ParseFailedException($"A node \"{expected}\" is expected but '{_reader.LastToken}' is provided", _reader.Position); } private ParseFailedException TokenExpectedError(char expected) { return new ParseFailedException($"A charactor '{expected}' is expected but '{_reader.LastToken}' is provided", _reader.Position); } } public class ParseFailedException : Exception { public ParseFailedException(string message, ulong pos) : base($"{message} (at position {pos})") { } } public sealed class JsonSerializer { private Type _type; public JsonSerializer(Type type) { _type = type; } public void Serialize(Stream s, T o, int indent = 0) { using JsonWriter writer = new JsonWriter(s, indent); SerializeValue(writer, o); } public string Serialize(T o, int indent = 0) { using MemoryStream memoryStream = new MemoryStream(); Serialize(memoryStream, o, indent); return Encoding.UTF8.GetString(memoryStream.ToArray()); } public INode SerializeToNode(T o) { byte[] buffer = null; using (MemoryStream memoryStream = new MemoryStream()) { Serialize(memoryStream, o); buffer = memoryStream.ToArray(); } using MemoryStream s = new MemoryStream(buffer); return new JsonDeserializer(typeof(INode)).Deserialize(s) as INode; } private void SerializeValue(JsonWriter writer, T o) { if (o is INode) { SerializeValue(writer, (o as INode).GenericContent); return; } switch (Node.KindOfValue(o)) { case NodeKind.String: case NodeKind.Integer: case NodeKind.Float: case NodeKind.Boolean: SerializePrimitive(writer, o); break; case NodeKind.Array: SerializeArray(writer, o); break; case NodeKind.Object: SerializeObject(writer, o); break; case NodeKind.Null: SerializeNull(writer, o); break; } } private void SerializePrimitive(JsonWriter writer, T o) { if (TypeHelper.TypeWrap(o.GetType()).IsEnum) { switch (TypeHelper.GetCustomAttribute(o.GetType())?.EnumConversion ?? EnumConversionType.AsInt) { case EnumConversionType.AsInt: SerializeValue(writer, Convert.ChangeType(o, Enum.GetUnderlyingType(o.GetType()))); break; case EnumConversionType.AsString: SerializeValue(writer, TypeHelper.GetStringEnumNameOf(o)); break; } } else { TypeHelper.TypeWrap(typeof(JsonWriter)).GetMethod("WriteValue", new Type[1] { o.GetType() }).Invoke(writer, new object[1] { o }); } } private void SerializeArray(JsonWriter writer, T o) { writer.WriteArrayStart(); foreach (object item in TypeHelper.ToIEnumerable(o)) { SerializeValue(writer, item); } writer.WriteArrayEnd(); } private void SerializeObject(JsonWriter writer, T o) { writer.WriteObjectStart(); foreach (KeyValuePair item in TypeHelper.ToKeyValues(o)) { writer.WriteObjectKey(item.Key); SerializeValue(writer, item.Value); } writer.WriteObjectEnd(); } private void SerializeNull(JsonWriter writer, T o) { writer.WriteValueNull(); } public object Deserialize(string text) { return new JsonDeserializer(_type).Deserialize(text); } public object Deserialize(Stream s) { return new JsonDeserializer(_type).Deserialize(s); } } public sealed class JsonWriter : IDisposable { private struct State { public StateKind Kind; public int Depth; } private enum StateKind { ObjectKeyHead, ObjectKeyOther, ObjectValue, ArrayHead, ArrayOther, None } private StreamWriter _writer; private int _indent; private string _indentStr; private Stack _states = new Stack(); public JsonWriter(Stream s, int indent = 0) { _writer = new StreamWriter(s); _indent = indent; if (_indent > 0) { _indentStr = new string(' ', _indent); } _states.Push(new State { Kind = StateKind.None, Depth = 0 }); } public void Dispose() { if (_writer != null) { ((IDisposable)_writer).Dispose(); } } public void WriteObjectStart() { State state = _states.Peek(); if (state.Kind == StateKind.ObjectKeyHead || state.Kind == StateKind.ObjectKeyOther) { throw new Exception(""); } WriteDelimiter(); _writer.Write("{"); _states.Push(new State { Kind = StateKind.ObjectKeyHead, Depth = state.Depth + 1 }); } public void WriteObjectKey(string key) { State state = _states.Peek(); if (state.Kind != 0 && state.Kind != StateKind.ObjectKeyOther) { throw new Exception(""); } WriteValue(key); _writer.Write(":"); _states.Pop(); _states.Push(new State { Kind = StateKind.ObjectValue, Depth = state.Depth }); } public void WriteObjectEnd() { State state = _states.Peek(); if (state.Kind != 0 && state.Kind != StateKind.ObjectKeyOther) { throw new Exception(""); } _states.Pop(); if (state.Kind == StateKind.ObjectKeyOther) { WriteIndentBreakForHuman(_states.Peek().Depth); } _writer.Write("}"); } public void WriteArrayStart() { State state = _states.Peek(); if (state.Kind == StateKind.ObjectKeyHead || state.Kind == StateKind.ObjectKeyOther) { throw new Exception(""); } WriteDelimiter(); _writer.Write("["); _states.Push(new State { Kind = StateKind.ArrayHead, Depth = state.Depth + 1 }); } public void WriteArrayEnd() { State state = _states.Peek(); if (state.Kind != StateKind.ArrayHead && state.Kind != StateKind.ArrayOther) { throw new Exception(""); } _states.Pop(); if (state.Kind == StateKind.ArrayOther) { WriteIndentBreakForHuman(_states.Peek().Depth); } _writer.Write("]"); } public void WriteValue(bool v) { WriteDelimiter(); _writer.Write(v ? "true" : "false"); } public void WriteValue(byte v) { WritePrimitive(v); } public void WriteValue(sbyte v) { WritePrimitive(v); } public void WriteValue(char v) { WritePrimitive(v); } public void WriteValue(decimal v) { WritePrimitive(v); } public void WriteValue(double v) { WritePrimitive(v); } public void WriteValue(float v) { WritePrimitive(v); } public void WriteValue(int v) { WritePrimitive(v); } public void WriteValue(uint v) { WritePrimitive(v); } public void WriteValue(long v) { WritePrimitive(v); } public void WriteValue(ulong v) { WritePrimitive(v); } public void WriteValue(short v) { WritePrimitive(v); } public void WriteValue(ushort v) { WritePrimitive(v); } public void WriteValue(string v) { WriteDelimiter(); _writer.Write('"'); _writer.Write(Escape(v).ToArray()); _writer.Write('"'); } public void WriteValueNull() { WriteDelimiter(); _writer.Write("null"); } private void WritePrimitive(char v) { WritePrimitive((int)v); } private void WritePrimitive(float v) { WritePrimitive($"{v:G9}"); } private void WritePrimitive(double v) { WritePrimitive($"{v:G17}"); } private void WritePrimitive(T v) { WriteDelimiter(); _writer.Write(v); } private void WriteIndentBreakForHuman(int depth) { if (_indent > 0) { _writer.Write('\n'); for (int i = 0; i < depth; i++) { _writer.Write(_indentStr); } } } private void WriteSpaceForHuman() { if (_indent > 0) { _writer.Write(' '); } } private void WriteDelimiter() { State state = _states.Peek(); if (state.Kind == StateKind.ArrayHead) { WriteIndentBreakForHuman(state.Depth); _states.Pop(); _states.Push(new State { Kind = StateKind.ArrayOther, Depth = state.Depth }); return; } if (state.Kind == StateKind.ObjectKeyHead) { WriteIndentBreakForHuman(state.Depth); } if (state.Kind == StateKind.ArrayOther || state.Kind == StateKind.ObjectKeyOther) { _writer.Write(","); WriteIndentBreakForHuman(state.Depth); } if (state.Kind == StateKind.ObjectValue) { WriteSpaceForHuman(); _states.Pop(); _states.Push(new State { Kind = StateKind.ObjectKeyOther, Depth = state.Depth }); } } private IEnumerable Escape(string s) { foreach (char c in s) { char modified = '\0'; if (c <= ' ' || c == '"' || c == '\\') { switch (c) { case '"': modified = '"'; break; case '\\': modified = '\\'; break; case '\b': modified = 'b'; break; case '\n': modified = 'n'; break; case '\r': modified = 'r'; break; case '\t': modified = 't'; break; } } if (modified != 0) { yield return '\\'; yield return modified; } else { yield return c; } } } } public enum NodeKind { Object, Array, String, Integer, Float, Boolean, Null, Undefined } public struct NodeKindWrapped { public NodeKind Kind; public bool Wrapped; } public interface INode { NodeKind Kind { get; } INode this[int index] { get; } INode this[string key] { get; } object GenericContent { get; } } public sealed class BooleanNode : INode { public static NodeKind KIND = NodeKind.Boolean; public static Type TYPE = typeof(bool); public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] => UndefinedNode.Undef; public bool Value { get; private set; } public object GenericContent => Value; public BooleanNode(bool v) { Value = v; } public override bool Equals(object rhsObj) { if (!(rhsObj is BooleanNode booleanNode)) { return false; } return Value.Equals(booleanNode.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return "BOOLEAN: " + Value; } } public sealed class NullNode : INode { public static NodeKind KIND = NodeKind.Null; public static Type TYPE = typeof(object); public static readonly INode Null = new NullNode(); public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] => UndefinedNode.Undef; public object GenericContent => null; public override bool Equals(object rhsObj) { if (!(rhsObj is NullNode)) { return false; } return true; } public override int GetHashCode() { return 0; } public override string ToString() { return "NULL"; } } public sealed class UndefinedNode : INode { public static NodeKind KIND = NodeKind.Undefined; public static Type TYPE = typeof(object); public static readonly INode Undef = new UndefinedNode(); public NodeKind Kind => KIND; public INode this[int index] => Undef; public INode this[string key] => Undef; public object GenericContent => null; public override bool Equals(object rhsObj) { if (!(rhsObj is UndefinedNode)) { return false; } return true; } public override int GetHashCode() { return 0; } public override string ToString() { return "UNDEFINED"; } } public sealed class IntegerNode : INode { public static NodeKind KIND = NodeKind.Integer; public static Type TYPE = typeof(long); public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] => UndefinedNode.Undef; public long Value { get; private set; } public object GenericContent => Value; public IntegerNode(long v) { Value = v; } public override bool Equals(object rhsObj) { if (!(rhsObj is IntegerNode integerNode)) { return false; } return Value.Equals(integerNode.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return "NUMBER(Int): " + Value; } } public sealed class FloatNode : INode { public static NodeKind KIND = NodeKind.Float; public static Type TYPE = typeof(double); public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] => UndefinedNode.Undef; public double Value { get; private set; } public object GenericContent => Value; public FloatNode(double v) { Value = v; } public override bool Equals(object rhsObj) { if (!(rhsObj is FloatNode floatNode)) { return false; } return Value.Equals(floatNode.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return "NUMBER(Float): " + Value; } } public sealed class StringNode : INode { public static NodeKind KIND = NodeKind.String; public static Type TYPE = typeof(string); public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] => UndefinedNode.Undef; public string Value { get; private set; } public object GenericContent => Value; public StringNode(string v) { Value = v; } public override bool Equals(object rhsObj) { if (!(rhsObj is StringNode stringNode)) { return false; } return Value.Equals(stringNode.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return "STRING: " + Value; } } public sealed class ObjectNode : INode, IEnumerable>, IEnumerable { public static NodeKind KIND = NodeKind.Object; public static Type TYPE = typeof(Dictionary); public Dictionary Elems; public NodeKind Kind => KIND; public INode this[int index] => UndefinedNode.Undef; public INode this[string key] { get { INode value = null; if (Elems != null) { Elems.TryGetValue(key, out value); } if (value == null) { return UndefinedNode.Undef; } return value; } } public object GenericContent { get { if (Elems == null) { return new Dictionary(); } return Elems; } } public ObjectNode() { } public ObjectNode(Dictionary v) { Elems = v; } public void AddElement(string key, INode elem) { if (Elems == null) { Elems = new Dictionary(); } Elems.Add(key, elem); } public void RemoveElement(string key) { if (Elems != null) { Elems.Remove(key); } } public IEnumerator> GetEnumerator() { if (Elems != null) { return Elems.OrderBy((KeyValuePair p) => p.Key).GetEnumerator(); } return Enumerable.Empty>().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override bool Equals(object rhsObj) { if (!(rhsObj is ObjectNode objectNode)) { return false; } if (Elems == null) { return objectNode.Elems == null; } return Elems.OrderBy((KeyValuePair p) => p.Key).SequenceEqual(objectNode.Elems.OrderBy((KeyValuePair p) => p.Key)); } public override int GetHashCode() { if (Elems == null) { return 0; } return Elems.GetHashCode(); } public override string ToString() { if (Elems == null) { return "OBJECT: {}"; } return "OBJECT: " + string.Join("; ", (from p in Elems orderby p.Key select p.Key + " = " + p.Value).ToArray()); } } public sealed class ArrayNode : INode, IEnumerable, IEnumerable { public static NodeKind KIND = NodeKind.Array; public static Type TYPE = typeof(List); public List Elems; public NodeKind Kind => KIND; public INode this[int index] { get { INode node = ((Elems != null) ? Elems.ElementAtOrDefault(index) : null); if (node == null) { return UndefinedNode.Undef; } return node; } } public INode this[string key] => UndefinedNode.Undef; public object GenericContent { get { if (Elems == null) { return new List(); } return Elems; } } public ArrayNode() { } public ArrayNode(List v) { Elems = v; } public void AddElement(INode elem) { if (Elems == null) { Elems = new List(); } Elems.Add(elem); } public void RemoveElementAt(int index) { if (Elems != null && index >= 0 && index < Elems.Count) { Elems.RemoveAt(index); } } public IEnumerator GetEnumerator() { if (Elems != null) { return Elems.GetEnumerator(); } return Enumerable.Empty().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override bool Equals(object rhsObj) { if (!(rhsObj is ArrayNode arrayNode)) { return false; } if (Elems == null) { return arrayNode.Elems == null; } return Elems.SequenceEqual(arrayNode.Elems); } public override int GetHashCode() { if (Elems == null) { return 0; } return Elems.GetHashCode(); } public override string ToString() { if (Elems == null) { return "ARRAY: []"; } return "ARRAY: " + string.Join("; ", Elems.Select((INode e) => e.ToString()).ToArray()); } } public static class Node { private static Dictionary _primitiveTable = new Dictionary { { typeof(bool), NodeKind.Boolean }, { typeof(byte), NodeKind.Integer }, { typeof(sbyte), NodeKind.Integer }, { typeof(char), NodeKind.Integer }, { typeof(decimal), NodeKind.Integer }, { typeof(double), NodeKind.Float }, { typeof(float), NodeKind.Float }, { typeof(int), NodeKind.Integer }, { typeof(uint), NodeKind.Integer }, { typeof(long), NodeKind.Integer }, { typeof(ulong), NodeKind.Integer }, { typeof(short), NodeKind.Integer }, { typeof(ushort), NodeKind.Integer }, { typeof(string), NodeKind.String } }; private static Dictionary _nodeKindTable = new Dictionary { { typeof(INode), ObjectNode.KIND }, { typeof(BooleanNode), BooleanNode.KIND }, { typeof(NullNode), NullNode.KIND }, { typeof(UndefinedNode), UndefinedNode.KIND }, { typeof(IntegerNode), IntegerNode.KIND }, { typeof(FloatNode), FloatNode.KIND }, { typeof(StringNode), StringNode.KIND }, { typeof(ObjectNode), ObjectNode.KIND }, { typeof(ArrayNode), ArrayNode.KIND } }; private static Dictionary _nodeTypeTable = new Dictionary { { BooleanNode.KIND, BooleanNode.TYPE }, { NullNode.KIND, NullNode.TYPE }, { UndefinedNode.KIND, UndefinedNode.TYPE }, { IntegerNode.KIND, IntegerNode.TYPE }, { FloatNode.KIND, FloatNode.TYPE }, { StringNode.KIND, StringNode.TYPE }, { ObjectNode.KIND, ObjectNode.TYPE }, { ArrayNode.KIND, ArrayNode.TYPE } }; public static NodeKind KindOfValue(T o) { if (o == null) { return NodeKind.Null; } return KindOfType(o.GetType()); } public static NodeKindWrapped KindOfTypeWrapped(Type ty) { NodeKindWrapped result; if (TypeHelper.TypeWrap(typeof(INode)).IsAssignableFrom(ty)) { result = default(NodeKindWrapped); result.Kind = _nodeKindTable[ty]; result.Wrapped = true; return result; } result = default(NodeKindWrapped); result.Kind = KindOfType(ty); result.Wrapped = false; return result; } public static NodeKind KindOfType(Type ty) { if (TypeHelper.TypeWrap(ty).IsGenericType && ty.GetGenericTypeDefinition() == typeof(Nullable<>)) { return KindOfType(TypeHelper.TypeWrap(ty).GetGenericArguments()[0]); } if (_primitiveTable.TryGetValue(ty, out var value)) { return value; } if (TypeHelper.TypeWrap(ty).IsEnum) { JsonAttribute customAttribute = TypeHelper.GetCustomAttribute(ty); if (customAttribute == null || customAttribute.EnumConversion != EnumConversionType.AsString) { return NodeKind.Integer; } return NodeKind.String; } if (TypeHelper.ElemTypeOfIEnumerable(ty) != null) { return NodeKind.Array; } return NodeKind.Object; } public static Type ValueTypeOfKind(NodeKind kind) { return _nodeTypeTable[kind]; } } internal static class TypeHelper { private struct RankedKey { public int Order; public string Key; } private class RankedKeyValueComparer : IComparer> { public int Compare(KeyValuePair a, KeyValuePair b) { throw new NotImplementedException(); } int IComparer>.Compare(KeyValuePair a, KeyValuePair b) { if (a.Key.Order != b.Key.Order) { return a.Key.Order - b.Key.Order; } return string.Compare(a.Key.Key, b.Key.Key); } } private class DeepEqualityComparer : EqualityComparer { public override bool Equals(object a, object b) { return DeepEquals(a, b); } public override int GetHashCode(object a) { return a.GetHashCode(); } } public delegate bool Converter(object input, out object output); private static readonly Dictionary> _convTable = new Dictionary> { { typeof(bool), new Dictionary { { typeof(bool), null } } }, { typeof(double), new Dictionary { { typeof(decimal), delegate(object i, out object o) { return ConvertFromDoubleToDecimal((double)i, out o); } }, { typeof(double), null }, { typeof(float), delegate(object i, out object o) { return ConvertFromDoubleToFloat((double)i, out o); } } } }, { typeof(long), new Dictionary { { typeof(byte), delegate(object i, out object o) { return ConvertFromLongToByte((long)i, out o); } }, { typeof(sbyte), delegate(object i, out object o) { return ConvertFromLongToSbyte((long)i, out o); } }, { typeof(char), delegate(object i, out object o) { return ConvertFromLongToChar((long)i, out o); } }, { typeof(decimal), delegate(object i, out object o) { return ConvertFromLongToDecimal((long)i, out o); } }, { typeof(double), delegate(object i, out object o) { return ConvertFromLongToDouble((long)i, out o); } }, { typeof(float), delegate(object i, out object o) { return ConvertFromLongToFloat((long)i, out o); } }, { typeof(int), delegate(object i, out object o) { return ConvertFromLongToInt((long)i, out o); } }, { typeof(uint), delegate(object i, out object o) { return ConvertFromLongToUint((long)i, out o); } }, { typeof(long), null }, { typeof(ulong), delegate(object i, out object o) { return ConvertFromLongToUlong((long)i, out o); } }, { typeof(short), delegate(object i, out object o) { return ConvertFromLongToShort((long)i, out o); } }, { typeof(ushort), delegate(object i, out object o) { return ConvertFromLongToUshort((long)i, out o); } } } }, { typeof(string), new Dictionary { { typeof(string), null } } } }; public static TypeInfo TypeWrap(Type ty) { return ty.GetTypeInfo(); } public static bool IsBoxed(Type ty) { TypeInfo typeInfo = TypeWrap(ty); if (typeInfo.IsClass) { return true; } if (typeInfo.IsGenericType) { return ty.GetGenericTypeDefinition() == typeof(Nullable<>); } return false; } public static T GetCustomAttribute(FieldInfo fi) where T : Attribute { return (T)(from a in fi.GetCustomAttributes(typeof(T), inherit: false) where a.GetType() == typeof(T) select a).FirstOrDefault(); } public static T GetCustomAttribute(Type ty) where T : Attribute { return (T)(from a in TypeWrap(ty).GetCustomAttributes(typeof(T), inherit: false) where a.GetType() == typeof(T) select a).FirstOrDefault(); } public static string[] GetStringEnumNames(Type ty) { return TypeWrap(ty).GetFields(BindingFlags.Static | BindingFlags.Public).Select(delegate(FieldInfo fi) { JsonFieldAttribute customAttribute = GetCustomAttribute(fi); return (customAttribute != null && customAttribute.Name != null) ? customAttribute.Name : fi.Name; }).ToArray(); } public static string GetStringEnumNameOf(object e) { Type type = e.GetType(); int num = Array.IndexOf(Enum.GetValues(type), e); return GetStringEnumNames(type)[num]; } public static IEnumerable ToIEnumerable(object o) { Type type = o.GetType(); if (type.IsArray) { if (type.HasElementType && TypeWrap(type.GetElementType()).IsClass) { return (IEnumerable)o; } return ((IEnumerable)o).Cast(); } return ((IEnumerable)o).Cast(); } public static Type ElemTypeOfIEnumerable(Type ty) { if (ty.IsArray) { if (ty.HasElementType) { return ty.GetElementType(); } return null; } if (TypeWrap(ty).IsGenericType && ty.GetGenericTypeDefinition() == typeof(List<>)) { return TypeWrap(ty).GetGenericArguments()[0]; } return null; } public static IEnumerable> ToKeyValues(object o) { List> list = ToRankedKeyValuesUnordered(o).ToList(); list.Sort(new RankedKeyValueComparer()); return list.Select((KeyValuePair v) => new KeyValuePair(v.Key.Key, v.Value)); } public static IEnumerable GetSerializableFields(Type ty) { TypeInfo typeInfo = TypeWrap(ty); FieldInfo[] fields = typeInfo.GetFields(BindingFlags.Instance | BindingFlags.Public); IEnumerable second = from field in typeInfo.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) where GetCustomAttribute(field) != null select field; return fields.Concat(second); } private static IEnumerable> ToRankedKeyValuesUnordered(object o) { Type type = o.GetType(); if (TypeWrap(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { if (TypeWrap(type).GetGenericArguments()[0] != typeof(string)) { throw new NotImplementedException(); } foreach (DictionaryEntry item in (IDictionary)o) { yield return new KeyValuePair(new RankedKey { Order = 0, Key = (string)item.Key }, item.Value); } yield break; } IEnumerable serializableFields = GetSerializableFields(type); foreach (FieldInfo item2 in serializableFields) { JsonFieldAttribute customAttribute = GetCustomAttribute(item2); string key = JsonFieldAttribute.FieldName(customAttribute, item2); object value = item2.GetValue(o); if (!JsonFieldIgnorableAttribute.IsIgnorable(GetCustomAttribute(item2), value)) { yield return new KeyValuePair(new RankedKey { Order = JsonFieldAttribute.FieldOrder(customAttribute), Key = key }, value); } } } public static bool DeepEquals(object lhs, object rhs) { NodeKind nodeKind = Node.KindOfValue(lhs); NodeKind nodeKind2 = Node.KindOfValue(rhs); if (nodeKind != nodeKind2) { return false; } switch (nodeKind) { case NodeKind.String: case NodeKind.Integer: case NodeKind.Float: case NodeKind.Boolean: return object.Equals(lhs, rhs); case NodeKind.Array: { IEnumerable first = ToIEnumerable(lhs); IEnumerable second = ToIEnumerable(rhs); return first.SequenceEqual(second, new DeepEqualityComparer()); } case NodeKind.Object: { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in ToKeyValues(lhs)) { dictionary.Add(item.Key, item.Value); } Dictionary rhsKvs = new Dictionary(); foreach (KeyValuePair item2 in ToKeyValues(rhs)) { rhsKvs.Add(item2.Key, item2.Value); } if (!dictionary.Keys.SequenceEqual(rhsKvs.Keys)) { return false; } return dictionary.All((KeyValuePair kv) => DeepEquals(kv.Value, rhsKvs[kv.Key])); } case NodeKind.Null: return true; default: throw new NotImplementedException(); } } private static bool ConvertFromDoubleToDecimal(double i, out object o) { try { o = (decimal)i; return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromDoubleToFloat(double i, out object o) { try { o = (float)i; return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToByte(long i, out object o) { try { if (i < 0) { throw new OverflowException(); } o = checked((byte)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToSbyte(long i, out object o) { try { o = checked((sbyte)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToChar(long i, out object o) { try { if (i < 0) { throw new OverflowException(); } o = (char)checked((ushort)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToDecimal(long i, out object o) { try { o = (decimal)i; return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToDouble(long i, out object o) { try { o = (double)i; return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToFloat(long i, out object o) { try { o = (float)i; return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToInt(long i, out object o) { try { o = checked((int)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToUint(long i, out object o) { try { if (i < 0) { throw new OverflowException(); } o = checked((uint)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToUlong(long i, out object o) { try { if (i < 0) { throw new OverflowException(); } o = checked((ulong)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToShort(long i, out object o) { try { o = checked((short)i); return true; } catch (OverflowException) { o = null; return false; } } private static bool ConvertFromLongToUshort(long i, out object o) { try { if (i < 0) { throw new OverflowException(); } o = checked((ushort)i); return true; } catch (OverflowException) { o = null; return false; } } public static bool GetConverter(Type fromTy, Type toTy, out Converter converter) { if (!_convTable.TryGetValue(fromTy, out var value)) { converter = null; return false; } return value.TryGetValue(toTy, out converter); } } } namespace VJson.Schema { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = false)] public class JsonSchemaAttribute : PreserveAttribute { public string Schema; public string Id; public string Ref; public string Title; public string Description; public double MultipleOf = double.MinValue; public double Maximum = double.MinValue; public double ExclusiveMaximum = double.MinValue; public double Minimum = double.MaxValue; public double ExclusiveMinimum = double.MaxValue; public int MaxLength = int.MinValue; public int MinLength = int.MaxValue; public string Pattern; public int MaxItems = int.MinValue; public int MinItems = int.MaxValue; public bool UniqueItems; public int MaxProperties = int.MinValue; public int MinProperties = int.MaxValue; public string[] Required; } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class ItemsJsonSchemaAttribute : JsonSchemaAttribute { } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class JsonSchemaRequiredAttribute : PreserveAttribute { } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class JsonSchemaDependenciesAttribute : PreserveAttribute { public string[] Dependencies { get; private set; } public JsonSchemaDependenciesAttribute(params string[] deps) { Dependencies = deps; } } public enum InfluenceRange { Entiry, AdditionalProperties } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public class JsonSchemaRefAttribute : PreserveAttribute { public Type TagType { get; private set; } public InfluenceRange Influence { get; private set; } public JsonSchemaRefAttribute(Type tagType, InfluenceRange influence = InfluenceRange.Entiry) { if (!RefChecker.IsRefTagDerived(tagType, out var _)) { throw new ArgumentException("IRefTag must be derived by tagType"); } TagType = tagType; Influence = influence; } } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class ItemsJsonSchemaRefAttribute : JsonSchemaRefAttribute { public ItemsJsonSchemaRefAttribute(Type tagType, InfluenceRange influence = InfluenceRange.Entiry) : base(tagType, influence) { } } [Json(ImplicitConstructable = true)] public sealed class JsonSchema { [JsonField(Name = "$schema", Order = -10)] [JsonFieldIgnorable] public string Schema; [JsonField(Name = "$id", Order = -11)] [JsonFieldIgnorable] public string Id; [JsonField(Name = "$ref", Order = -12)] [JsonFieldIgnorable] public string Ref; [JsonField(Name = "title", Order = 0)] [JsonFieldIgnorable] public string Title; [JsonField(Name = "description", Order = 1)] [JsonFieldIgnorable] public string Description; [JsonField(Name = "type", TypeHints = new Type[] { typeof(string), typeof(string[]) }, Order = 10)] [JsonFieldIgnorable] public object Type; [JsonField(Name = "enum", Order = 11)] [JsonFieldIgnorable] public object[] Enum; [JsonField(Name = "const", Order = 12)] [JsonFieldIgnorable] public object Const; [JsonField(Name = "multipleOf", Order = 20)] [JsonFieldIgnorable(WhenValueIs = double.MinValue)] public double MultipleOf = double.MinValue; [JsonField(Name = "maximum", Order = 21)] [JsonFieldIgnorable(WhenValueIs = double.MinValue)] public double Maximum = double.MinValue; [JsonField(Name = "exclusiveMaximum", Order = 22)] [JsonFieldIgnorable(WhenValueIs = double.MinValue)] public double ExclusiveMaximum = double.MinValue; [JsonField(Name = "minimum", Order = 23)] [JsonFieldIgnorable(WhenValueIs = double.MaxValue)] public double Minimum = double.MaxValue; [JsonField(Name = "exclusiveMinimum", Order = 24)] [JsonFieldIgnorable(WhenValueIs = double.MaxValue)] public double ExclusiveMinimum = double.MaxValue; [JsonField(Name = "maxLength", Order = 30)] [JsonFieldIgnorable(WhenValueIs = int.MinValue)] public int MaxLength = int.MinValue; [JsonField(Name = "minLength", Order = 31)] [JsonFieldIgnorable(WhenValueIs = int.MaxValue)] public int MinLength = int.MaxValue; [JsonField(Name = "pattern", Order = 32)] [JsonFieldIgnorable] public string Pattern; [JsonField(Name = "items", TypeHints = new Type[] { typeof(JsonSchema), typeof(JsonSchema[]) }, Order = 40)] [JsonFieldIgnorable] private object items; [JsonField(Name = "additionalItems", Order = 41)] [JsonFieldIgnorable] public JsonSchema AdditionalItems; [JsonField(Name = "maxItems", Order = 42)] [JsonFieldIgnorable(WhenValueIs = int.MinValue)] public int MaxItems = int.MinValue; [JsonField(Name = "minItems", Order = 43)] [JsonFieldIgnorable(WhenValueIs = int.MaxValue)] public int MinItems = int.MaxValue; [JsonField(Name = "uniqueItems", Order = 44)] [JsonFieldIgnorable(WhenValueIs = false)] public bool UniqueItems; [JsonField(Name = "maxProperties", Order = 50)] [JsonFieldIgnorable(WhenValueIs = int.MinValue)] public int MaxProperties = int.MinValue; [JsonField(Name = "minProperties", Order = 51)] [JsonFieldIgnorable(WhenValueIs = int.MaxValue)] public int MinProperties = int.MaxValue; [JsonField(Name = "required", Order = 52)] [JsonFieldIgnorable] public string[] Required; [JsonField(Name = "properties", Order = 53)] [JsonFieldIgnorable] public Dictionary Properties; [JsonField(Name = "patternProperties", Order = 54)] [JsonFieldIgnorable] public Dictionary PatternProperties; [JsonField(Name = "additionalProperties", Order = 55)] [JsonFieldIgnorable] public JsonSchema AdditionalProperties; [JsonField(Name = "dependencies", TypeHints = new Type[] { typeof(Dictionary), typeof(Dictionary) }, Order = 56)] [JsonFieldIgnorable] public object Dependencies; [JsonField(Name = "allOf", Order = 70)] [JsonFieldIgnorable] public List AllOf; [JsonField(Name = "anyOf", Order = 71)] [JsonFieldIgnorable] public List AnyOf; [JsonField(Name = "oneOf", Order = 72)] [JsonFieldIgnorable] public List OneOf; [JsonField(Name = "not", Order = 73)] [JsonFieldIgnorable] public JsonSchema Not; public object Items => items; public JsonSchema TypedItems { set { items = value; } } private bool EqualsOnlyAny(JsonSchema rhs) { if (EqualsSingletonOrArray(Type, rhs.Type) && EqualsEnumerable(Enum, rhs.Enum)) { return object.Equals(Const, rhs.Const); } return false; } private bool EqualsOnlyNum(JsonSchema rhs) { if (MultipleOf == rhs.MultipleOf && Maximum == rhs.Maximum && ExclusiveMaximum == rhs.ExclusiveMaximum && Minimum == rhs.Minimum) { return ExclusiveMinimum == rhs.ExclusiveMinimum; } return false; } private bool EqualsOnlyString(JsonSchema rhs) { if (MaxLength == rhs.MaxLength && MinLength == rhs.MinLength) { return object.Equals(Pattern, rhs.Pattern); } return false; } private bool EqualsOnlyArray(JsonSchema rhs) { if (EqualsSingletonOrArray(Items, rhs.Items) && object.Equals(AdditionalItems, rhs.AdditionalItems) && MaxItems == rhs.MaxItems && MinItems == rhs.MinItems) { return UniqueItems == rhs.UniqueItems; } return false; } private bool EqualsOnlyObject(JsonSchema rhs) { return true; } private void AddToAllOf(JsonSchema s) { if (AllOf == null) { AllOf = new List(); } AllOf.Add(s); } private void AddToAnyOf(JsonSchema s) { if (AnyOf == null) { AnyOf = new List(); } AnyOf.Add(s); } private void AddToOneOf(JsonSchema s) { if (OneOf == null) { OneOf = new List(); } OneOf.Add(s); } private bool EqualsOnlySubBool(JsonSchema rhs) { if (EqualsEnumerable(AllOf, rhs.AllOf) && EqualsEnumerable(AnyOf, rhs.AnyOf) && EqualsEnumerable(OneOf, rhs.OneOf)) { return object.Equals(Not, rhs.Not); } return false; } public override bool Equals(object rhsObj) { if (!(rhsObj is JsonSchema jsonSchema)) { return false; } if (Title == jsonSchema.Title && Description == jsonSchema.Description && EqualsOnlyAny(jsonSchema) && EqualsOnlyNum(jsonSchema) && EqualsOnlyString(jsonSchema) && EqualsOnlyArray(jsonSchema) && EqualsOnlyObject(jsonSchema)) { return EqualsOnlySubBool(jsonSchema); } return false; } public override int GetHashCode() { throw new NotImplementedException(); } public override string ToString() { return new JsonSerializer(typeof(JsonSchema)).Serialize(this); } public JsonSchema() { } [Preserve] public JsonSchema(bool b) { if (!b) { Not = new JsonSchema(); } } public static JsonSchema CreateFromType(JsonSchemaRegistry reg = null, bool asRef = false) { return CreateFromType(typeof(T), reg, asRef); } public static JsonSchema CreateFromType(Type ty, JsonSchemaRegistry reg = null, bool asRef = false) { string text; JsonSchema jsonSchema2; switch (Node.KindOfType(ty)) { case NodeKind.Boolean: return new JsonSchema { Type = "boolean" }; case NodeKind.Integer: { object[] enum2 = null; if (TypeHelper.TypeWrap(ty).IsEnum) { enum2 = System.Enum.GetValues(ty).Cast().ToArray(); } return new JsonSchema { Type = "integer", Enum = enum2 }; } case NodeKind.Float: return new JsonSchema { Type = "number" }; case NodeKind.String: { object[] @enum = null; if (TypeHelper.TypeWrap(ty).IsEnum) { object[] stringEnumNames = TypeHelper.GetStringEnumNames(ty); @enum = stringEnumNames; } return new JsonSchema { Type = "string", Enum = @enum }; } case NodeKind.Array: { Type type = TypeHelper.ElemTypeOfIEnumerable(ty); return new JsonSchema { Type = "array", TypedItems = ((type != null) ? CreateFromType(type, reg, asRef: true) : null) }; } case NodeKind.Object: { if (ty == typeof(object)) { return new JsonSchema(); } if (TypeHelper.TypeWrap(ty).IsGenericType && ty.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { return new JsonSchema { Type = "object" }; } if (reg == null) { reg = new JsonSchemaRegistry(); } JsonSchemaAttribute customAttribute = TypeHelper.GetCustomAttribute(ty); text = customAttribute?.Id; if (string.IsNullOrEmpty(text)) { text = ty.ToString(); } JsonSchema jsonSchema = reg.Resolve(text); if (jsonSchema != null) { if (asRef) { return new JsonSchema { Ref = text }; } return jsonSchema; } jsonSchema2 = CreateFromSchemaAttr(customAttribute); jsonSchema2.Type = "object"; Type baseType = TypeHelper.TypeWrap(ty).BaseType; HashSet hashSet = null; if (baseType != null) { if (RefChecker.IsRefTag(baseType, out var elemType)) { JsonSchema jsonSchema3 = CreateFromType(elemType, reg); jsonSchema2.Type = jsonSchema3.Type; goto IL_0581; } JsonSchema jsonSchema4 = CreateFromType(baseType, reg, asRef: true); if (jsonSchema4 != null && jsonSchema4.Ref != null) { jsonSchema2.AddToAllOf(jsonSchema4); hashSet = new HashSet(from f in TypeHelper.GetSerializableFields(baseType) select f.Name); } } Dictionary dictionary = new Dictionary(); List list = new List(); Dictionary dictionary2 = new Dictionary(); foreach (FieldInfo serializableField in TypeHelper.GetSerializableFields(ty)) { Type fieldType = serializableField.FieldType; string text2 = JsonFieldAttribute.FieldName(TypeHelper.GetCustomAttribute(serializableField), serializableField); if (hashSet != null && hashSet.Contains(serializableField.Name)) { dictionary.Add(text2, new JsonSchema()); continue; } JsonSchema jsonSchema5 = CreateFromSchemaAttr(TypeHelper.GetCustomAttribute(serializableField)); ItemsJsonSchemaAttribute customAttribute2 = TypeHelper.GetCustomAttribute(serializableField); if (customAttribute2 != null) { jsonSchema5.TypedItems = CreateFromSchemaAttr(customAttribute2); } if (TypeHelper.GetCustomAttribute(serializableField) != null) { list.Add(text2); } JsonSchemaDependenciesAttribute customAttribute3 = TypeHelper.GetCustomAttribute(serializableField); if (customAttribute3 != null) { dictionary2.Add(text2, customAttribute3.Dependencies); } JsonSchema jsonSchema6 = CreateFromType(fieldType, reg, asRef: true); if (jsonSchema6.Ref != null) { jsonSchema5 = jsonSchema6; } else { if (jsonSchema5.Type == null) { jsonSchema5.Type = jsonSchema6.Type; } if (jsonSchema5.Enum == null) { jsonSchema5.Enum = jsonSchema6.Enum; } if (jsonSchema6.Items != null) { JsonSchema jsonSchema7 = jsonSchema6.Items as JsonSchema; if (jsonSchema7.Ref != null) { jsonSchema5.TypedItems = jsonSchema7; } else { if (jsonSchema7.Type != null) { if (jsonSchema5.Items is JsonSchema jsonSchema8) { jsonSchema8.Type = jsonSchema7.Type; } else { jsonSchema5.TypedItems = new JsonSchema { Type = jsonSchema7.Type }; } } if (jsonSchema7.Enum != null) { (jsonSchema5.Items as JsonSchema).Enum = jsonSchema7.Enum; } } } } JsonSchemaRefAttribute customAttribute4 = TypeHelper.GetCustomAttribute(serializableField); if (customAttribute4 != null) { if (!RefChecker.IsRefTagDerived(customAttribute4.TagType, out var _)) { throw new ArgumentException("IRefTag must be derived by tagType"); } JsonSchema s = CreateFromType(customAttribute4.TagType, reg, asRef: true); switch (customAttribute4.Influence) { case InfluenceRange.Entiry: jsonSchema5.AddToAllOf(s); break; case InfluenceRange.AdditionalProperties: if (jsonSchema5.AdditionalProperties == null) { jsonSchema5.AdditionalProperties = new JsonSchema(); } jsonSchema5.AdditionalProperties.AddToAllOf(s); break; } } ItemsJsonSchemaRefAttribute customAttribute5 = TypeHelper.GetCustomAttribute(serializableField); if (customAttribute5 != null) { if (!RefChecker.IsRefTagDerived(customAttribute5.TagType, out var _)) { throw new ArgumentException("IRefTag must be derived by tagType"); } JsonSchema s2 = CreateFromType(customAttribute5.TagType, reg, asRef: true); switch (customAttribute5.Influence) { case InfluenceRange.Entiry: if (jsonSchema5.Items == null) { jsonSchema5.TypedItems = new JsonSchema(); } ((JsonSchema)jsonSchema5.Items).AddToAllOf(s2); break; case InfluenceRange.AdditionalProperties: if (jsonSchema5.Items == null) { jsonSchema5.TypedItems = new JsonSchema(); } if (((JsonSchema)jsonSchema5.Items).AdditionalProperties == null) { ((JsonSchema)jsonSchema5.Items).AdditionalProperties = new JsonSchema(); } ((JsonSchema)jsonSchema5.Items).AdditionalProperties.AddToAllOf(s2); break; } } dictionary.Add(text2, jsonSchema5); } jsonSchema2.Properties = dictionary; if (list.Count != 0) { jsonSchema2.Required = list.ToArray(); } if (dictionary2.Count != 0) { jsonSchema2.Dependencies = dictionary2; } goto IL_0581; } default: { throw new NotImplementedException(); } IL_0581: reg.Register(text, jsonSchema2); if (asRef) { return new JsonSchema { Ref = text }; } return jsonSchema2; } } private static JsonSchema CreateFromSchemaAttr(JsonSchemaAttribute attr) { JsonSchema jsonSchema = new JsonSchema(); if (attr == null) { return jsonSchema; } jsonSchema.Schema = attr.Schema; jsonSchema.Id = attr.Id; jsonSchema.Ref = attr.Ref; jsonSchema.Title = attr.Title; jsonSchema.Description = attr.Description; jsonSchema.MultipleOf = attr.MultipleOf; jsonSchema.Maximum = attr.Maximum; jsonSchema.ExclusiveMaximum = attr.ExclusiveMaximum; jsonSchema.Minimum = attr.Minimum; jsonSchema.ExclusiveMinimum = attr.ExclusiveMinimum; jsonSchema.MaxLength = attr.MaxLength; jsonSchema.MinLength = attr.MinLength; jsonSchema.Pattern = attr.Pattern; jsonSchema.MaxItems = attr.MaxItems; jsonSchema.MinItems = attr.MinItems; jsonSchema.UniqueItems = attr.UniqueItems; jsonSchema.MaxProperties = attr.MaxProperties; jsonSchema.MinProperties = attr.MinProperties; jsonSchema.Required = attr.Required; return jsonSchema; } private static bool EqualsSingletonOrArray(object lhs, object rhs) where T : class { if (lhs == null && rhs == null) { return true; } if (lhs == null || rhs == null) { return false; } T[] array = lhs as T[]; T[] array2 = rhs as T[]; if (array != null && array2 != null) { return EqualsEnumerable(array, array2); } T objA = lhs as T; T objB = rhs as T; return object.Equals(objA, objB); } private static bool EqualsEnumerable(IEnumerable lhs, IEnumerable rhs) { if (lhs != null || rhs != null) { if (lhs != null && lhs != null) { return lhs.SequenceEqual(rhs); } return false; } return true; } } public static class JsonSchemaExtensions { public static ConstraintsViolationException Validate(this JsonSchema j, object o, JsonSchemaRegistry reg = null) { return new JsonSchemaValidator(j).Validate(o, reg); } internal static ConstraintsViolationException Validate(this JsonSchema j, object o, State state, JsonSchemaRegistry reg) { return new JsonSchemaValidator(j).Validate(o, state, reg); } } public class RefTag where T : struct { } internal static class RefChecker { public static bool IsRefTagDerived(Type ty, out Type elemType) { Type baseType = TypeHelper.TypeWrap(ty).BaseType; if (baseType != null) { return IsRefTag(baseType, out elemType); } elemType = null; return false; } public static bool IsRefTag(Type ty, out Type elemType) { if (TypeHelper.TypeWrap(ty).IsGenericType && ty.GetGenericTypeDefinition() == typeof(RefTag<>)) { elemType = TypeHelper.TypeWrap(ty).GetGenericArguments()[0]; return true; } elemType = null; return false; } } public sealed class JsonSchemaRegistry { private readonly Dictionary _registory = new Dictionary(); public JsonSchema Resolve(string id) { JsonSchema value = null; if (_registory.TryGetValue(id, out value)) { return value; } return null; } public void Register(string id, JsonSchema j) { _registory.Add(id, j); } public IEnumerable GetRegisteredIDs() { return _registory.Keys; } } public sealed class JsonSchemaValidator { private readonly JsonSchema _schema; public JsonSchemaValidator(JsonSchema j) { _schema = j; } public ConstraintsViolationException Validate(object o, JsonSchemaRegistry reg = null) { return Validate(o, default(State), reg); } internal ConstraintsViolationException Validate(object o, State state, JsonSchemaRegistry reg) { if (_schema.Ref != null) { return (reg?.Resolve(_schema.Ref) ?? throw new Exception("Schema is not registered or registory is null: Ref=" + _schema.Ref)).Validate(o, state, reg); } ConstraintsViolationException ex = null; if (o is INode) { return Validate((o as INode).GenericContent, state, reg); } NodeKind nodeKind = Node.KindOfValue(o); if (_schema.Type != null) { if (_schema.Type.GetType().IsArray) { string[] array = (string[])_schema.Type; bool flag = false; string[] array2 = array; foreach (string typeName in array2) { if (ValidateKind(nodeKind, typeName)) { flag = true; break; } } if (!flag) { string text = nodeKind.ToString(); string text2 = string.Join(", ", array); return new ConstraintsViolationException(state.CreateMessage("Type is not contained(Actual: {0}; Expected: [{1}])", text, text2)); } } else { string text3 = (string)_schema.Type; if (!ValidateKind(nodeKind, text3)) { string text4 = nodeKind.ToString(); string text5 = text3.ToString(); return new ConstraintsViolationException(state.CreateMessage("Type is not matched(Actual: {0}; Expected: {1})", text4, text5)); } } } if (_schema.Enum != null) { object lhs = o; if (o != null && TypeHelper.TypeWrap(o.GetType()).IsEnum && nodeKind == NodeKind.String) { lhs = TypeHelper.GetStringEnumNameOf(o); } bool flag2 = false; object[] @enum = _schema.Enum; foreach (object rhs in @enum) { if (TypeHelper.DeepEquals(lhs, rhs)) { flag2 = true; break; } } if (!flag2) { return new ConstraintsViolationException(state.CreateMessage("Enum is not matched")); } } if (_schema.Not != null) { ex = _schema.Not.Validate(o, state, reg); if (ex == null) { return new ConstraintsViolationException(state.CreateMessage("Not")); } } if (_schema.AllOf != null) { int num = 0; foreach (JsonSchema item in _schema.AllOf) { ex = item.Validate(o, state, reg); if (ex != null) { return new ConstraintsViolationException(state.CreateMessage("AllOf[{0}] is failed", num), ex); } num++; } } if (_schema.AnyOf != null) { bool flag3 = false; foreach (JsonSchema item2 in _schema.AnyOf) { ex = item2.Validate(o, state, reg); if (ex == null) { flag3 = true; break; } } if (!flag3) { return new ConstraintsViolationException(state.CreateMessage("None of AnyOf is matched")); } } if (_schema.OneOf != null) { int num2 = -1; int num3 = 0; foreach (JsonSchema item3 in _schema.OneOf) { ex = item3.Validate(o, state, reg); if (ex == null) { if (num2 != -1) { return new ConstraintsViolationException(state.CreateMessage("Both of OneOf[{0}] and OneOf[{1}] are matched", num2, num3)); } num2 = num3; } num3++; } if (num2 == -1) { return new ConstraintsViolationException(state.CreateMessage("None of AnyOf is matched")); } } switch (nodeKind) { case NodeKind.Integer: case NodeKind.Float: ex = ValidateNumber(Convert.ToDouble(o), state, reg); if (ex != null) { return new ConstraintsViolationException("Number", ex); } break; case NodeKind.String: { string v = ((o != null && TypeHelper.TypeWrap(o.GetType()).IsEnum) ? TypeHelper.GetStringEnumNameOf(o) : ((string)o)); ex = ValidateString(v, state, reg); if (ex != null) { return new ConstraintsViolationException("String", ex); } break; } case NodeKind.Array: ex = ValidateArray(TypeHelper.ToIEnumerable(o), state, reg); if (ex != null) { return new ConstraintsViolationException("Array", ex); } break; case NodeKind.Object: ex = ValidateObject(o, state, reg); if (ex != null) { return new ConstraintsViolationException("Object", ex); } break; default: throw new NotImplementedException(nodeKind.ToString()); case NodeKind.Boolean: case NodeKind.Null: break; } return null; } private ConstraintsViolationException ValidateNumber(double v, State state, JsonSchemaRegistry reg) { if (_schema.MultipleOf != double.MinValue) { if (_schema.MultipleOf <= 0.0) { throw new InvalidOperationException("MultipleOf must be greater than 0: Value = " + _schema.MultipleOf); } double num = v / _schema.MultipleOf; if (num != Math.Truncate(num)) { return new ConstraintsViolationException(state.CreateMessage("MultipleOf assertion !({0} % {1} == 0)", v, _schema.MultipleOf)); } } if (_schema.Maximum != double.MinValue && !(v <= _schema.Maximum)) { return new ConstraintsViolationException(state.CreateMessage("Maximum assertion !({0} <= {1})", v, _schema.Maximum)); } if (_schema.ExclusiveMaximum != double.MinValue && !(v < _schema.ExclusiveMaximum)) { return new ConstraintsViolationException(state.CreateMessage("ExclusiveMaximum assertion !({0} < {1})", v, _schema.ExclusiveMaximum)); } if (_schema.Minimum != double.MaxValue && !(v >= _schema.Minimum)) { return new ConstraintsViolationException(state.CreateMessage("Minimum assertion !({0} >= {1})", v, _schema.Minimum)); } if (_schema.ExclusiveMinimum != double.MaxValue && !(v > _schema.ExclusiveMinimum)) { return new ConstraintsViolationException(state.CreateMessage("ExclusiveMinimum assertion !({0} > {1})", v, _schema.ExclusiveMinimum)); } return null; } private ConstraintsViolationException ValidateString(string v, State state, JsonSchemaRegistry reg) { StringInfo stringInfo = null; if (_schema.MaxLength != int.MinValue) { stringInfo = stringInfo ?? new StringInfo(v); if (stringInfo.LengthInTextElements > _schema.MaxLength) { return new ConstraintsViolationException(state.CreateMessage("MaxLength assertion !({0} <= {1})", stringInfo.LengthInTextElements, _schema.MaxLength)); } } if (_schema.MinLength != int.MaxValue) { stringInfo = stringInfo ?? new StringInfo(v); if (stringInfo.LengthInTextElements < _schema.MinLength) { return new ConstraintsViolationException(state.CreateMessage("MinLength assertion !({0} >= {1})", stringInfo.LengthInTextElements, _schema.MinLength)); } } if (_schema.Pattern != null && !Regex.IsMatch(v, _schema.Pattern)) { return new ConstraintsViolationException(state.CreateMessage("Pattern assertion !(\"{0}\" matched \"{1}\")", v, _schema.Pattern)); } return null; } private ConstraintsViolationException ValidateArray(IEnumerable vsIter, State state, JsonSchemaRegistry reg) { object[] array = vsIter.ToArray(); int num = array.Length; if (_schema.MaxItems != int.MinValue && num > _schema.MaxItems) { return new ConstraintsViolationException(state.CreateMessage("MaxItems assertion !({0} <= {1})", num, _schema.MaxItems)); } if (_schema.MinItems != int.MaxValue && num < _schema.MinItems) { return new ConstraintsViolationException(state.CreateMessage("MinItems assertion !({0} >= {1})", num, _schema.MinItems)); } if (_schema.UniqueItems) { for (int i = 0; i < array.Length; i++) { for (int j = 0; j < array.Length; j++) { if (i != j && TypeHelper.DeepEquals(array[i], array[j])) { return new ConstraintsViolationException(state.CreateMessage("UniqueItems assertion: Elements at {0} and {1} are duplicated", i, j)); } } } } List list = null; if (_schema.Items != null) { if (_schema.Items.GetType().IsArray) { JsonSchema[] source = (JsonSchema[])_schema.Items; int num2 = 0; object[] array2 = array; foreach (object obj in array2) { JsonSchema jsonSchema = source.ElementAtOrDefault(num2); if (jsonSchema == null) { if (list == null) { list = new List(); } list.Add(obj); continue; } ConstraintsViolationException ex = jsonSchema.Validate(obj, state.NestAsElem(num2), reg); if (ex != null) { return new ConstraintsViolationException("Items", ex); } num2++; } } else { JsonSchema j2 = (JsonSchema)_schema.Items; int num3 = 0; object[] array2 = array; foreach (object o in array2) { ConstraintsViolationException ex2 = j2.Validate(o, state.NestAsElem(num3), reg); if (ex2 != null) { return new ConstraintsViolationException("Items", ex2); } num3++; } } } if (_schema.AdditionalItems != null && list != null) { foreach (object item in list) { ConstraintsViolationException ex3 = _schema.AdditionalItems.Validate(item, state, reg); if (ex3 != null) { return new ConstraintsViolationException("AdditionalItems", ex3); } } } return null; } private ConstraintsViolationException ValidateObject(object v, State state, JsonSchemaRegistry reg) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in TypeHelper.ToKeyValues(v)) { ConstraintsViolationException ex = ValidateObjectField(item.Key, item.Value, state.NestAsElem(item.Key), reg); if (ex != null) { return ex; } dictionary.Add(item.Key, item.Value); } if (_schema.Required != null) { HashSet hashSet = new HashSet(_schema.Required); hashSet.IntersectWith(dictionary.Keys); if (hashSet.Count != _schema.Required.Count()) { string text = string.Join(", ", hashSet.ToArray()); string text2 = string.Join(", ", _schema.Required); return new ConstraintsViolationException(state.CreateMessage("Lack of required fields(Actual: [{0}]; Expected: [{1}])", text, text2)); } } if (_schema.MaxProperties != int.MinValue && dictionary.Count > _schema.MaxProperties) { return new ConstraintsViolationException(state.CreateMessage("MaxProperties assertion !({0} <= {1})", dictionary.Count, _schema.MaxProperties)); } if (_schema.MinProperties != int.MaxValue && dictionary.Count < _schema.MinProperties) { return new ConstraintsViolationException(state.CreateMessage("MaxProperties assertion !({0} >= {1})", dictionary.Count, _schema.MinProperties)); } if (_schema.Dependencies != null) { if (_schema.Dependencies is Dictionary dictionary2) { foreach (KeyValuePair item2 in dictionary) { string[] value = null; if (dictionary2.TryGetValue(item2.Key, out value)) { IEnumerable source = ((string[])value.Clone()).Intersect(dictionary.Keys); if (source.Count() != value.Count()) { string text3 = string.Join(", ", source.ToArray()); string text4 = string.Join(", ", value); return new ConstraintsViolationException(state.CreateMessage("Dependencies assertion. Lack of depended fields for {0}(Actual: [{1}]; Expected: [{2}])", item2.Key, text3, text4)); } } } } else if (_schema.Dependencies is Dictionary dictionary3) { foreach (KeyValuePair item3 in dictionary) { JsonSchema value2 = null; if (dictionary3.TryGetValue(item3.Key, out value2)) { ConstraintsViolationException ex2 = value2.Validate(v, default(State).NestAsElem(item3.Key), reg); if (ex2 != null) { return new ConstraintsViolationException(state.CreateMessage("Dependencies assertion. Failed to validation for {0}", item3.Key), ex2); } } } } } return null; } private ConstraintsViolationException ValidateObjectField(string key, object value, State state, JsonSchemaRegistry reg) { bool flag = false; if (_schema.Properties != null) { JsonSchema value2 = null; if (_schema.Properties.TryGetValue(key, out value2)) { flag = true; ConstraintsViolationException ex = value2.Validate(value, state, reg); if (ex != null) { return new ConstraintsViolationException("Property", ex); } } } if (_schema.PatternProperties != null) { foreach (KeyValuePair patternProperty in _schema.PatternProperties) { if (Regex.IsMatch(key, patternProperty.Key)) { flag = true; ConstraintsViolationException ex2 = patternProperty.Value.Validate(value, state, reg); if (ex2 != null) { return new ConstraintsViolationException("PatternProperties", ex2); } } } } if (_schema.AdditionalProperties != null && !flag) { ConstraintsViolationException ex3 = _schema.AdditionalProperties.Validate(value, state, reg); if (ex3 != null) { return new ConstraintsViolationException("AdditionalProperties", ex3); } } return null; } private static bool ValidateKind(NodeKind kind, string typeName) { switch (typeName) { case "null": return kind == NodeKind.Null; case "boolean": return kind == NodeKind.Boolean; case "object": return kind == NodeKind.Object; case "array": return kind == NodeKind.Array; case "number": if (kind != NodeKind.Integer) { return kind == NodeKind.Float; } return true; case "string": return kind == NodeKind.String; case "integer": return kind == NodeKind.Integer; default: throw new NotImplementedException(); } } } public class ConstraintsViolationException : Exception { public ConstraintsViolationException(string message) : base(message) { } public ConstraintsViolationException(string message, ConstraintsViolationException inner) : base($"{message}.{inner.Message}") { } } } namespace VJson.Internal { internal struct State { private string _elemName; private string ElemName { get { if (string.IsNullOrEmpty(_elemName)) { return "(root)"; } return _elemName; } } internal State NestAsElem(int elem) { State result = default(State); result._elemName = $"{ElemName}[{elem}]"; return result; } internal State NestAsElem(string elem) { State result = default(State); result._elemName = $"{ElemName}[\"{elem}\"]"; return result; } internal string CreateMessage(string format, params object[] args) { return $"{ElemName}: {string.Format(format, args)}."; } } internal static class StateExtension { public static string CreateNodeConversionFailureMessage(this State s, INode fromNode, IEnumerable toTypes) { string toDetail = string.Format("one of [{0}]", string.Join(", ", toTypes.Select((Type t) => t.ToString()).ToArray())); return s.CreateNodeConversionFailureMessage(fromNode, toDetail); } public static string CreateNodeConversionFailureMessage(this State s, INode fromNode, Type toType) { string text = toType.ToString(); if (fromNode.Kind == NodeKind.Null && !TypeHelper.IsBoxed(toType)) { text = $"non-boxed value({text})"; } return s.CreateNodeConversionFailureMessage(fromNode, text); } private static string CreateNodeConversionFailureMessage(this State s, INode fromNode, string toDetail) { string text = $"{fromNode.Kind} node"; switch (fromNode.Kind) { case NodeKind.Boolean: text = $"{text} ({((BooleanNode)fromNode).Value})"; break; case NodeKind.Integer: text = $"{text} ({((IntegerNode)fromNode).Value})"; break; case NodeKind.Float: text = $"{text} ({((FloatNode)fromNode).Value})"; break; } return s.CreateMessage("{0} cannot convert to {1}", text, toDetail); } public static string CreateTypeConversionFailureMessage(this State s, T fromValue, Type toType, string reason = null) { string arg = $"{typeof(T).ToString()} value"; NodeKind nodeKind = Node.KindOfType(typeof(T)); if ((uint)(nodeKind - 3) <= 2u) { arg = $"{arg} ({fromValue})"; } string text = $"{arg} cannot convert to {toType}"; if (reason != null) { text = $"{text} (Reason: {reason})"; } return s.CreateMessage(text); } } }