/// <summary> /// Downloads and registers a schema at the specified URI. /// </summary> public static IJsonSchema Get(string uri) { IJsonSchema schema; lock (_schemaLookup) { uri = uri.TrimEnd('#'); if (!_schemaLookup.TryGetValue(uri, out schema)) { var schemaJson = JsonSchemaOptions.Download(uri); var schemaValue = JsonValue.Parse(schemaJson); schema = JsonSchemaFactory.FromJson(schemaValue, new Uri(uri)); var metaSchemas = new IJsonSchema[] { JsonSchema07.MetaSchema, JsonSchema06.MetaSchema, JsonSchema04.MetaSchema }; SchemaValidationResults validation = null; if (schema.Schema != null) { var bySchema = metaSchemas.FirstOrDefault(s => s.Id == schema.Schema); if (bySchema != null) { validation = bySchema.Validate(schemaValue); } } else { foreach (var metaSchema in metaSchemas) { validation = metaSchema.Validate(schemaValue); if (validation.Valid) { break; } } } if (validation != null && !validation.Valid) { var errors = string.Join(Environment.NewLine, validation.Errors.Select(e => e.Message)); throw new ArgumentException($"The given path does not contain a valid schema. Errors: \n{errors}"); } _schemaLookup[uri] = schema; } } return(schema); }
/// <summary> /// Builds an object from a <see cref="JsonValue"/>. /// </summary> /// <param name="json">The <see cref="JsonValue"/> representation of the object.</param> /// <param name="serializer">The <see cref="JsonSerializer"/> instance to use for additional /// serialization of values.</param> public void FromJson(JsonValue json, JsonSerializer serializer) { if (json.Type == JsonValueType.Boolean) { if (json.Boolean) { Definition = JsonSchema04.Empty; } } else { Definition = JsonSchemaFactory.FromJson(json); } }
// TODO: This is a JSON pointer. Since JsonPatch uses it, it might be beneficial to implement as an object or at least reuse this. private IJsonSchema _ResolveLocalReference(JsonValue root, string path, Uri documentPath) { var properties = path.Split('/').Skip(1).ToList(); if (!properties.Any()) { return(JsonSchemaFactory.FromJson(root, _schemaFactory, documentPath)); } var value = root; foreach (var property in properties) { var unescaped = _Unescape(property); if (value.Type == JsonValueType.Object) { if (!value.Object.ContainsKey(unescaped)) { return(null); } JsonValue id; // There's not really another way to do this well without the reference knowing what // version schema it should be using at each step in the path, so we test for both. // Since draft-06's '$id' is less likely to be used as a regular JSON property, we // check it first. if (value.Object.TryGetValue("$id", out id) || value.Object.TryGetValue("id", out id)) { documentPath = Uri.TryCreate(id.String, UriKind.Absolute, out Uri uri) ? uri : new Uri(documentPath, id.String); } value = value.Object[unescaped]; } else if (value.Type == JsonValueType.Array) { if (!int.TryParse(unescaped, out int index) || index >= value.Array.Count) { return(null); } value = value.Array[index]; } } return(JsonSchemaFactory.FromJson(value, _schemaFactory, documentPath)); }
/// <summary> /// Builds an object from a <see cref="JsonValue"/>. /// </summary> /// <param name="json">The <see cref="JsonValue"/> representation of the object.</param> /// <param name="serializer">The <see cref="JsonSerializer"/> instance to use for additional serialization of values.</param> public virtual void FromJson(JsonValue json, JsonSerializer serializer) { var obj = json.Object; Id = obj.TryGetString("id"); var uriFolder = DocumentPath?.OriginalString.EndsWith("/") ?? true ? DocumentPath : DocumentPath?.GetParentUri(); if (!string.IsNullOrWhiteSpace(Id) && (Uri.TryCreate(Id, UriKind.Absolute, out Uri uri) || Uri.TryCreate(uriFolder + Id, UriKind.Absolute, out uri))) { DocumentPath = uri; JsonSchemaRegistry.Register(this); } Schema = obj.TryGetString("$schema"); Title = obj.TryGetString("title"); Description = obj.TryGetString("description"); if (obj.ContainsKey("default")) { Default = obj["default"]; } MultipleOf = obj.TryGetNumber("multipleOf"); Maximum = obj.TryGetNumber("maximum"); ExclusiveMaximum = obj.TryGetBoolean("exclusiveMaximum"); Minimum = obj.TryGetNumber("minimum"); ExclusiveMinimum = obj.TryGetBoolean("exclusiveMinimum"); MaxLength = (uint?)obj.TryGetNumber("maxLength"); MinLength = (uint?)obj.TryGetNumber("minLength"); Pattern = obj.TryGetString("pattern"); if (obj.ContainsKey("additionalItems")) { if (obj["additionalItems"].Type == JsonValueType.Boolean) { AdditionalItems = obj["additionalItems"].Boolean ? AdditionalItems.True : AdditionalItems.False; } else { AdditionalItems = new AdditionalItems { Definition = _ReadSchema(obj["additionalItems"]) } }; } MaxItems = (uint?)obj.TryGetNumber("maxItems"); MinItems = (uint?)obj.TryGetNumber("minItems"); if (obj.ContainsKey("items")) { Items = JsonSchemaFactory.FromJson(obj["items"], DocumentPath); } UniqueItems = obj.TryGetBoolean("uniqueItems"); MaxProperties = (uint?)obj.TryGetNumber("maxProperties"); MinProperties = (uint?)obj.TryGetNumber("minProperties"); if (obj.ContainsKey("properties")) { Properties = obj["properties"].Object.ToDictionary(kvp => kvp.Key, kvp => _ReadSchema(kvp.Value)); } Required = obj.TryGetArray("required")?.Select(jv => jv.String).ToList(); if (obj.ContainsKey("additionalProperties")) { if (obj["additionalProperties"].Type == JsonValueType.Boolean) { AdditionalProperties = obj["additionalProperties"].Boolean ? AdditionalProperties.True : AdditionalProperties.False; } else { AdditionalProperties = new AdditionalProperties { Definition = _ReadSchema(obj["additionalProperties"]) } }; } if (obj.ContainsKey("definitions")) { Definitions = obj["definitions"].Object.ToDictionary(kvp => kvp.Key, kvp => _ReadSchema(kvp.Value)); } if (obj.ContainsKey("patternProperties")) { var patterns = obj["patternProperties"].Object; PatternProperties = patterns.ToDictionary(kvp => new Regex(kvp.Key), kvp => _ReadSchema(kvp.Value)); } if (obj.ContainsKey("dependencies")) { Dependencies = obj["dependencies"].Object.Select(v => { IJsonSchemaDependency dependency; switch (v.Value.Type) { case JsonValueType.Object: dependency = new SchemaDependency(v.Key, _ReadSchema(v.Value)); break; case JsonValueType.Array: if (!v.Value.Array.Any()) { throw new ArgumentException("Property dependency must declare at least one property."); } dependency = new PropertyDependency(v.Key, v.Value.Array.Select(jv => jv.String)); break; default: throw new ArgumentOutOfRangeException(); } return(dependency); }); } if (obj.ContainsKey("enum")) { Enum = json.Object["enum"].Array.Select(jv => new EnumSchemaValue(jv)); } if (obj.ContainsKey("type")) { Type = obj["type"].FromJson(); } if (obj.ContainsKey("allOf")) { AllOf = obj["allOf"].Array.Select(_ReadSchema); } if (obj.ContainsKey("anyOf")) { AnyOf = json.Object["anyOf"].Array.Select(_ReadSchema); } if (obj.ContainsKey("oneOf")) { OneOf = obj["oneOf"].Array.Select(_ReadSchema); } if (obj.ContainsKey("not")) { Not = _ReadSchema(obj["not"]); } var formatKey = obj.TryGetString("format"); Format = StringFormat.GetFormat(formatKey); var details = obj.Where(kvp => !_definedProperties.Contains(kvp.Key)).ToJson(); if (details.Any()) { ExtraneousDetails = details; } }
private IJsonSchema _ReadSchema(JsonValue json) { return(JsonSchemaFactory.FromJson <JsonSchema04>(json, DocumentPath)); }
private IJsonSchema _ReadSchema(JsonValue json) { return(JsonSchemaFactory.FromJson(json, () => new JsonSchema06(), DocumentPath)); }
/// <summary> /// Builds an object from a <see cref="JsonValue"/>. /// </summary> /// <param name="json">The <see cref="JsonValue"/> representation of the object.</param> /// <param name="serializer">The <see cref="JsonSerializer"/> instance to use for additional /// serialization of values.</param> public void FromJson(JsonValue json, JsonSerializer serializer) { AddRange(json.Array.Select(j => JsonSchemaFactory.FromJson(j, DocumentPath))); }