<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />

JsonSchemaBuilder

using Newtonsoft.Json.Linq; using Newtonsoft.Json.Utilities; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace Newtonsoft.Json.Schema { [Obsolete("JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details.")] internal class JsonSchemaBuilder { private readonly IList<JsonSchema> _stack; private readonly JsonSchemaResolver _resolver; private readonly IDictionary<string, JsonSchema> _documentSchemas; private JsonSchema _currentSchema; private JObject _rootSchema; private JsonSchema CurrentSchema => _currentSchema; public JsonSchemaBuilder(JsonSchemaResolver resolver) { _stack = new List<JsonSchema>(); _documentSchemas = new Dictionary<string, JsonSchema>(); _resolver = resolver; } private void Push(JsonSchema value) { _currentSchema = value; _stack.Add(value); _resolver.LoadedSchemas.Add(value); _documentSchemas.Add(value.Location, value); } private JsonSchema Pop() { JsonSchema currentSchema = _currentSchema; _stack.RemoveAt(_stack.Count - 1); _currentSchema = _stack.LastOrDefault(); return currentSchema; } internal JsonSchema Read(JsonReader reader) { JToken jToken = JToken.ReadFrom(reader); _rootSchema = (jToken as JObject); JsonSchema jsonSchema = BuildSchema(jToken); ResolveReferences(jsonSchema); return jsonSchema; } private string UnescapeReference(string reference) { return Uri.UnescapeDataString(reference).Replace("~1", "/").Replace("~0", "~"); } private JsonSchema ResolveReferences(JsonSchema schema) { if (schema.DeferredReference != null) { string text = schema.DeferredReference; bool flag = text.StartsWith("#", StringComparison.Ordinal); if (flag) text = UnescapeReference(text); JsonSchema jsonSchema = _resolver.GetSchema(text); if (jsonSchema == null) { if (flag) { string[] array = schema.DeferredReference.TrimStart(new char[1] { '#' }).Split(new char[1] { '/' }, StringSplitOptions.RemoveEmptyEntries); JToken jToken = _rootSchema; string[] array2 = array; foreach (string reference in array2) { string text2 = UnescapeReference(reference); if (jToken.Type == JTokenType.Object) jToken = jToken[text2]; else if (jToken.Type == JTokenType.Array || jToken.Type == JTokenType.Constructor) { int result; jToken = ((!int.TryParse(text2, out result) || result < 0 || result >= jToken.Count()) ? null : jToken[result]); } if (jToken == null) break; } if (jToken != null) jsonSchema = BuildSchema(jToken); } if (jsonSchema == null) throw new JsonException("Could not resolve schema reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, schema.DeferredReference)); } schema = jsonSchema; } if (schema.ReferencesResolved) return schema; schema.ReferencesResolved = true; if (schema.Extends != null) { for (int j = 0; j < schema.Extends.Count; j++) { schema.Extends[j] = ResolveReferences(schema.Extends[j]); } } if (schema.Items != null) { for (int k = 0; k < schema.Items.Count; k++) { schema.Items[k] = ResolveReferences(schema.Items[k]); } } if (schema.AdditionalItems != null) schema.AdditionalItems = ResolveReferences(schema.AdditionalItems); List<KeyValuePair<string, JsonSchema>>.Enumerator enumerator; if (schema.PatternProperties != null) { enumerator = schema.PatternProperties.ToList().GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair<string, JsonSchema> current = enumerator.Current; schema.PatternProperties[current.Key] = ResolveReferences(current.Value); } } finally { ((IDisposable)enumerator).Dispose(); } } if (schema.Properties != null) { enumerator = schema.Properties.ToList().GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair<string, JsonSchema> current2 = enumerator.Current; schema.Properties[current2.Key] = ResolveReferences(current2.Value); } } finally { ((IDisposable)enumerator).Dispose(); } } if (schema.AdditionalProperties != null) schema.AdditionalProperties = ResolveReferences(schema.AdditionalProperties); return schema; } private JsonSchema BuildSchema(JToken token) { JObject jObject = token as JObject; if (jObject == null) throw JsonException.Create(token, token.Path, "Expected object while parsing schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); if (jObject.TryGetValue("$ref", out JToken value)) return new JsonSchema { DeferredReference = (string)value }; string text = token.Path.Replace(".", "/").Replace("[", "/").Replace("]", string.Empty); if (!string.IsNullOrEmpty(text)) text = "/" + text; text = "#" + text; if (_documentSchemas.TryGetValue(text, out JsonSchema value2)) return value2; Push(new JsonSchema { Location = text }); ProcessSchemaProperties(jObject); return Pop(); } private void ProcessSchemaProperties(JObject schemaObject) { foreach (KeyValuePair<string, JToken> item in schemaObject) { switch (item.Key) { case "type": CurrentSchema.Type = ProcessType(item.Value); break; case "id": CurrentSchema.Id = (string)item.Value; break; case "title": CurrentSchema.Title = (string)item.Value; break; case "description": CurrentSchema.Description = (string)item.Value; break; case "properties": CurrentSchema.Properties = ProcessProperties(item.Value); break; case "items": ProcessItems(item.Value); break; case "additionalProperties": ProcessAdditionalProperties(item.Value); break; case "additionalItems": ProcessAdditionalItems(item.Value); break; case "patternProperties": CurrentSchema.PatternProperties = ProcessProperties(item.Value); break; case "required": CurrentSchema.Required = (bool)item.Value; break; case "requires": CurrentSchema.Requires = (string)item.Value; break; case "minimum": CurrentSchema.Minimum = (double)item.Value; break; case "maximum": CurrentSchema.Maximum = (double)item.Value; break; case "exclusiveMinimum": CurrentSchema.ExclusiveMinimum = (bool)item.Value; break; case "exclusiveMaximum": CurrentSchema.ExclusiveMaximum = (bool)item.Value; break; case "maxLength": CurrentSchema.MaximumLength = (int)item.Value; break; case "minLength": CurrentSchema.MinimumLength = (int)item.Value; break; case "maxItems": CurrentSchema.MaximumItems = (int)item.Value; break; case "minItems": CurrentSchema.MinimumItems = (int)item.Value; break; case "divisibleBy": CurrentSchema.DivisibleBy = (double)item.Value; break; case "disallow": CurrentSchema.Disallow = ProcessType(item.Value); break; case "default": CurrentSchema.Default = item.Value.DeepClone(); break; case "hidden": CurrentSchema.Hidden = (bool)item.Value; break; case "readonly": CurrentSchema.ReadOnly = (bool)item.Value; break; case "format": CurrentSchema.Format = (string)item.Value; break; case "pattern": CurrentSchema.Pattern = (string)item.Value; break; case "enum": ProcessEnum(item.Value); break; case "extends": ProcessExtends(item.Value); break; case "uniqueItems": CurrentSchema.UniqueItems = (bool)item.Value; break; } } } private void ProcessExtends(JToken token) { IList<JsonSchema> list = new List<JsonSchema>(); if (token.Type == JTokenType.Array) { foreach (JToken item in (IEnumerable<JToken>)token) { list.Add(BuildSchema(item)); } } else { JsonSchema jsonSchema = BuildSchema(token); if (jsonSchema != null) list.Add(jsonSchema); } if (list.Count > 0) CurrentSchema.Extends = list; } private void ProcessEnum(JToken token) { if (token.Type != JTokenType.Array) throw JsonException.Create(token, token.Path, "Expected Array token while parsing enum values, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); CurrentSchema.Enum = new List<JToken>(); foreach (JToken item in (IEnumerable<JToken>)token) { CurrentSchema.Enum.Add(item.DeepClone()); } } private void ProcessAdditionalProperties(JToken token) { if (token.Type == JTokenType.Boolean) CurrentSchema.AllowAdditionalProperties = (bool)token; else CurrentSchema.AdditionalProperties = BuildSchema(token); } private void ProcessAdditionalItems(JToken token) { if (token.Type == JTokenType.Boolean) CurrentSchema.AllowAdditionalItems = (bool)token; else CurrentSchema.AdditionalItems = BuildSchema(token); } private IDictionary<string, JsonSchema> ProcessProperties(JToken token) { IDictionary<string, JsonSchema> dictionary = new Dictionary<string, JsonSchema>(); if (token.Type != JTokenType.Object) throw JsonException.Create(token, token.Path, "Expected Object token while parsing schema properties, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); foreach (JProperty item in (IEnumerable<JToken>)token) { if (dictionary.ContainsKey(item.Name)) throw new JsonException("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, item.Name)); dictionary.Add(item.Name, BuildSchema(item.Value)); } return dictionary; } private void ProcessItems(JToken token) { CurrentSchema.Items = new List<JsonSchema>(); switch (token.Type) { case JTokenType.Object: CurrentSchema.Items.Add(BuildSchema(token)); CurrentSchema.PositionalItemsValidation = false; break; case JTokenType.Array: CurrentSchema.PositionalItemsValidation = true; foreach (JToken item in (IEnumerable<JToken>)token) { CurrentSchema.Items.Add(BuildSchema(item)); } break; default: throw JsonException.Create(token, token.Path, "Expected array or JSON schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); } } private JsonSchemaType? ProcessType(JToken token) { switch (token.Type) { case JTokenType.Array: { JsonSchemaType? nullable = JsonSchemaType.None; { foreach (JToken item in (IEnumerable<JToken>)token) { if (item.Type != JTokenType.String) throw JsonException.Create(item, item.Path, "Expected JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); nullable |= MapType((string)item); } return nullable; } } case JTokenType.String: return MapType((string)token); default: throw JsonException.Create(token, token.Path, "Expected array or JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); } } internal static JsonSchemaType MapType(string type) { if (!JsonSchemaConstants.JsonSchemaTypeMapping.TryGetValue(type, out JsonSchemaType value)) throw new JsonException("Invalid JSON schema type: {0}".FormatWith(CultureInfo.InvariantCulture, type)); return value; } internal static string MapType(JsonSchemaType type) { return JsonSchemaConstants.JsonSchemaTypeMapping.Single((KeyValuePair<string, JsonSchemaType> kv) => kv.Value == type).Key; } } }