/// <summary> /// Used register any subschemas during validation. Enables look-forward compatibility with `$ref` keywords. /// </summary> /// <param name="baseUri">The current base URI</param> /// <param name="localRegistry">A local schema registry to handle cases where <paramref name="baseUri"/> is null.</param> public void RegisterSubschemas(Uri baseUri, JsonSchemaRegistry localRegistry) { foreach (var schema in Values) { schema.RegisterSubschemas(baseUri, localRegistry); } }
private JsonValue _Resolve(JsonValue root) { var referenceParts = Reference.Split(new[] { '#' }, StringSplitOptions.None); var address = string.IsNullOrWhiteSpace(referenceParts[0]) ? DocumentPath?.OriginalString : referenceParts[0]; var fragment = referenceParts.Length > 1 ? referenceParts[1] : string.Empty; var jValue = root; if (!string.IsNullOrWhiteSpace(address)) { if (!Uri.TryCreate(address, UriKind.Absolute, out Uri absolute)) { address = Id + address; } if (DocumentPath != null && !Uri.TryCreate(address, UriKind.Absolute, out absolute)) { var uriFolder = DocumentPath.OriginalString.EndsWith("/") ? DocumentPath : DocumentPath.GetParentUri(); absolute = new Uri(uriFolder, address); address = absolute.OriginalString; } jValue = JsonSchemaRegistry.Get(address).ToJson(null); } if (jValue == null) { return(root); } if (jValue == "#") { throw new ArgumentException("Cannot use a root reference as the base schema."); } Resolved = _ResolveLocalReference(jValue, fragment, string.IsNullOrWhiteSpace(address) ? null : new Uri(address)); return(jValue); }
private void _ResolveReference(SchemaValidationContext context) { var documentPath = context.BaseUri; var referenceParts = Reference.Split(new[] { '#' }, StringSplitOptions.None); var address = string.IsNullOrWhiteSpace(referenceParts[0]) ? documentPath?.OriginalString : referenceParts[0]; _resolvedFragment = referenceParts.Length > 1 ? JsonPointer.Parse(referenceParts[1]) : new JsonPointer(); if (!string.IsNullOrWhiteSpace(address)) { if (!Uri.TryCreate(address, UriKind.Absolute, out var absolute)) { address = context.Local.Id + address; } if (documentPath != null && !Uri.TryCreate(address, UriKind.Absolute, out absolute)) { var uriFolder = documentPath.OriginalString.EndsWith("/") ? documentPath : documentPath.GetParentUri(); absolute = new Uri(uriFolder, address); address = absolute.OriginalString; } _resolvedRoot = JsonSchemaRegistry.Get(address); } else { _resolvedRoot = context.Root; } _ResolveLocalReference(_resolvedRoot?.DocumentPath ?? context.BaseUri); }
/// <summary> /// Creates a new instance of the <see cref="SchemaValidationContext"/> class by copying values from another instance. /// </summary> public SchemaValidationContext(SchemaValidationContext source) : this(source.Root, source.Instance, source.BaseRelativeLocation, source.RelativeLocation, source.InstanceLocation, source.Options) { Local = source.Local; Root = source.Root; RecursiveAnchor = source.RecursiveAnchor; Instance = source.Instance; ShouldTrackValidatedValues = source.ShouldTrackValidatedValues; _InitializeHashSet(ref _evaluatedPropertyNames, source._evaluatedPropertyNames); _InitializeHashSet(ref _locallyEvaluatedPropertyNames, source._locallyEvaluatedPropertyNames); _InitializeHashSet(ref _validatedIndices, source._validatedIndices); _InitializeHashSet(ref _locallyValidatedIndices, source._locallyValidatedIndices); LastEvaluatedIndex = source.LastEvaluatedIndex; LocalTierLastEvaluatedIndex = source.LocalTierLastEvaluatedIndex; BaseUri = source.BaseUri; InstanceLocation = source.InstanceLocation; RelativeLocation = source.RelativeLocation; BaseRelativeLocation = source.BaseRelativeLocation; IsMetaSchemaValidation = source.IsMetaSchemaValidation; LocalRegistry = source.LocalRegistry; Options = source.Options; }
private void _ResolveReference(SchemaValidationContext context) { if (context.RecursiveAnchor != null) { var baseDocument = JsonSchemaRegistry.Get(context.BaseUri.OriginalString); if (baseDocument?.Get <RecursiveAnchorKeyword>() != null) { _resolvedRoot = context.RecursiveAnchor; } } if (Reference.IsLocalSchemaId()) { Resolved = context.LocalRegistry.GetLocal(Reference); if (Resolved != null) { return; } } var documentPath = _resolvedRoot?.DocumentPath ?? context.BaseUri; var referenceParts = Reference.Split(new[] { '#' }, StringSplitOptions.None); var address = string.IsNullOrWhiteSpace(referenceParts[0]) ? documentPath?.OriginalString : referenceParts[0]; _resolvedFragment = referenceParts.Length > 1 ? JsonPointer.Parse(referenceParts[1]) : new JsonPointer(); if (_resolvedRoot == null) { if (!string.IsNullOrWhiteSpace(address)) { if (!Uri.TryCreate(address, UriKind.Absolute, out var absolute)) { address = context.Local.Id + address; } if (documentPath != null && !Uri.TryCreate(address, UriKind.Absolute, out absolute)) { var uriFolder = documentPath.OriginalString.EndsWith("/") ? documentPath : documentPath.GetParentUri(); absolute = new Uri(uriFolder, address); address = absolute.OriginalString; } _resolvedRoot = JsonSchemaRegistry.Get(address); } else { _resolvedRoot = context.Root; } } var wellKnown = JsonSchemaRegistry.GetWellKnown(Reference); if (wellKnown != null) { Resolved = wellKnown; return; } _ResolveLocalReference(_resolvedRoot?.DocumentPath ?? context.BaseUri); }
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. internal SchemaValidationContext(JsonSchema root, JsonValue instance, JsonPointer?baseRelativeLocation, JsonPointer relativeLocation, JsonPointer instanceLocation) #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. { Root = root; Instance = instance; BaseRelativeLocation = baseRelativeLocation; RelativeLocation = relativeLocation; InstanceLocation = instanceLocation; LocalRegistry = new JsonSchemaRegistry(); }
/// <summary> /// Provides the validation logic for this keyword. /// </summary> /// <param name="context">The context object.</param> /// <returns>Results object containing a final result and any errors that may have been found.</returns> public SchemaValidationResults Validate(SchemaValidationContext context) { if (!context.IsMetaSchemaValidation) { return(SchemaValidationResults.Null); } var nestedResults = new List <SchemaValidationResults>(); var allVocabularies = this.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); allVocabularies[SchemaVocabularies.Core] = true; foreach (var kvp in allVocabularies) { var vocabulary = kvp.Key; if (vocabulary.MetaSchemaId == context.Local.Id) { continue; } var required = kvp.Value; if (vocabulary.MetaSchemaId != null) { var newContext = new SchemaValidationContext(context) { BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, vocabulary.Id), RelativeLocation = context.RelativeLocation.CloneAndAppend(Name, vocabulary.Id), }; var metaSchema = JsonSchemaRegistry.Get(vocabulary.MetaSchemaId); if (metaSchema != null) { metaSchema.Validate(newContext); } else if (required) { nestedResults.Add(new SchemaValidationResults(Name, newContext)); } } } var results = new SchemaValidationResults(Name, context) { NestedResults = nestedResults, IsValid = nestedResults.All(r => r.IsValid) }; return(results); }
/// <summary> /// Creates a new instance of the <see cref="SchemaValidationContext"/> class by copying values from another instance. /// </summary> public SchemaValidationContext(SchemaValidationContext source) : this() { Local = source.Local; Root = source.Root; RecursiveAnchor = source.RecursiveAnchor; Instance = source.Instance; EvaluatedPropertyNames.AddRange(source.EvaluatedPropertyNames); LocallyEvaluatedPropertyNames.AddRange(source.LocallyEvaluatedPropertyNames); LastEvaluatedIndex = source.LastEvaluatedIndex; LocalTierLastEvaluatedIndex = source.LocalTierLastEvaluatedIndex; BaseUri = source.BaseUri; InstanceLocation = source.InstanceLocation; RelativeLocation = source.RelativeLocation; BaseRelativeLocation = source.BaseRelativeLocation; IsMetaSchemaValidation = source.IsMetaSchemaValidation; LocalRegistry = source.LocalRegistry; }
/// <summary> /// Used register any subschemas during validation. Enables look-forward compatibility with `$ref` keywords. /// </summary> /// <param name="baseUri">The current base URI</param> /// <param name="localRegistry">A local schema registry to handle cases where <paramref name="baseUri"/> is null.</param> public void RegisterSubschemas(Uri?baseUri, JsonSchemaRegistry localRegistry) { Value.RegisterSubschemas(baseUri, localRegistry); }
/// <summary> /// Used register any subschemas during validation. Enables look-forward compatibility with `$ref` keywords. /// </summary> /// <param name="baseUri">The current base URI</param> /// <param name="localRegistry"></param> public void RegisterSubschemas(Uri?baseUri, JsonSchemaRegistry localRegistry) { }
/// <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; } }
/// <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) { if (json.Type == JsonValueType.Boolean) { BooleanSchemaDefinition = json.Boolean; return; } serializer = serializer ?? _schemaSerializer; 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"); Comment = obj.TryGetString("$comment"); Title = obj.TryGetString("title"); Description = obj.TryGetString("description"); if (obj.ContainsKey("default")) { Default = obj["default"]; } ReadOnly = obj.TryGetBoolean("readOnly"); if (obj.ContainsKey("examples")) { Examples = json.Object["examples"].Array; Examples.EqualityStandard = ArrayEquality.ContentsEqual; } MultipleOf = obj.TryGetNumber("multipleOf"); Maximum = obj.TryGetNumber("maximum"); ExclusiveMaximum = obj.TryGetNumber("exclusiveMaximum"); Minimum = obj.TryGetNumber("minimum"); ExclusiveMinimum = obj.TryGetNumber("exclusiveMinimum"); MaxLength = (uint?)obj.TryGetNumber("maxLength"); MinLength = (uint?)obj.TryGetNumber("minLength"); Pattern = obj.TryGetString("pattern"); if (obj.ContainsKey("additionalItems")) { AdditionalItems = _ReadSchema(obj["additionalItems"]); } MaxItems = (uint?)obj.TryGetNumber("maxItems"); MinItems = (uint?)obj.TryGetNumber("minItems"); if (obj.ContainsKey("items")) { Items = _ReadSchema(obj["items"]); } UniqueItems = obj.TryGetBoolean("uniqueItems"); if (obj.ContainsKey("contains")) { Contains = _ReadSchema(obj["contains"]); } 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")) { AdditionalProperties = _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.Boolean: case JsonValueType.Object: dependency = new SchemaDependency(v.Key, _ReadSchema(v.Value)); break; case JsonValueType.Array: dependency = new PropertyDependency(v.Key, v.Value.Array.Select(jv => jv.String)); break; default: throw new ArgumentOutOfRangeException(); } return(dependency); }); } if (obj.ContainsKey("propertyNames")) { PropertyNames = _ReadSchema(obj["propertyNames"]); } if (obj.ContainsKey("const")) { Const = obj["const"]; } 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); ContentMediaType = obj.TryGetString("contentMediaType"); var options = serializer.Options; var newOptions = new JsonSerializerOptions(options) { CaseSensitiveDeserialization = false }; serializer.Options = newOptions; if (obj.ContainsKey("contentEncoding")) { ContentEncoding = serializer.Deserialize <ContentEncoding>(obj["contentEncoding"]); } serializer.Options = options; if (obj.ContainsKey("if")) { If = _ReadSchema(obj["if"]); } if (obj.ContainsKey("then")) { Then = _ReadSchema(obj["then"]); } if (obj.ContainsKey("else")) { Else = _ReadSchema(obj["else"]); } var details = obj.Where(kvp => !_definedProperties.Contains(kvp.Key)).ToJson(); if (details.Any()) { ExtraneousDetails = details; } }
internal SchemaValidationContext() { LocalRegistry = new JsonSchemaRegistry(); }
/// <summary> /// Used register any subschemas during validation. Enables look-forward compatibility with `$ref` keywords. /// </summary> /// <param name="baseUri">The current base URI</param> /// <param name="localRegistry"></param> /// <implementationNotes> /// If the dependency does not contain any schemas (e.g. `maximum`), this method is a no-op. /// </implementationNotes> public void RegisterSubschemas(Uri baseUri, JsonSchemaRegistry localRegistry) { _schema.RegisterSubschemas(baseUri, localRegistry); }
private void _ResolveReference(SchemaValidationContext context) { Log.Schema($"Resolving `{Reference}`"); if (context.RecursiveAnchor != null) { Log.Schema("Finding anchor of root schema"); if (context.BaseUri == null) { throw new InvalidOperationException("BaseUri not set"); } var baseDocument = JsonSchemaRegistry.Get(context.BaseUri.OriginalString); if (baseDocument?.Get <RecursiveAnchorKeyword>() != null) { _resolvedRoot = context.RecursiveAnchor; } } if (Reference.IsLocalSchemaId()) { Log.Schema("Reference recognized as anchor or local ID"); Resolved = context.LocalRegistry.GetLocal(Reference); if (Resolved != null) { return; } Log.Schema($"`{Reference}` is an unknown anchor"); } var documentPath = _resolvedRoot?.DocumentPath ?? context.BaseUri; var referenceParts = Reference.Split(new[] { '#' }, StringSplitOptions.None); var address = string.IsNullOrWhiteSpace(referenceParts[0]) ? documentPath?.OriginalString : referenceParts[0]; _resolvedFragment = referenceParts.Length > 1 ? JsonPointer.Parse(referenceParts[1]) : new JsonPointer(); if (_resolvedRoot == null) { if (!string.IsNullOrWhiteSpace(address)) { if (!Uri.TryCreate(address, UriKind.Absolute, out _)) { address = context.Local.Id + address; } if (documentPath != null && !Uri.TryCreate(address, UriKind.Absolute, out _)) { var uriFolder = documentPath.OriginalString.EndsWith("/") ? documentPath : documentPath.GetParentUri(); var absolute = new Uri(uriFolder, address); address = absolute.OriginalString; } _resolvedRoot = JsonSchemaRegistry.Get(address); } else { _resolvedRoot = context.Root; } } if (_resolvedRoot == null) { Log.Schema("Could not resolve root of reference"); return; } var wellKnown = JsonSchemaRegistry.GetWellKnown(Reference); if (wellKnown != null) { Log.Schema("Well known reference found"); Resolved = wellKnown; return; } _ResolveLocalReference(_resolvedRoot?.DocumentPath ?? context.BaseUri !); }
private void _ResolveReference(SchemaValidationContext context) { Log.Schema($"Resolving `{Reference}`"); if (Reference.IsLocalSchemaId()) { Log.Schema("Reference recognized as anchor or local ID"); Resolved = context.LocalRegistry.GetLocal(Reference); if (Resolved != null) { return; } Log.Schema($"`{Reference}` is an unknown anchor"); } var documentPath = context.BaseUri; var referenceParts = Reference.Split(new[] { '#' }, StringSplitOptions.None); var address = string.IsNullOrWhiteSpace(referenceParts[0]) ? documentPath?.OriginalString.Split('#')[0] : referenceParts[0]; _resolvedFragment = referenceParts.Length > 1 ? JsonPointer.Parse(referenceParts[1]) : new JsonPointer(); if (!string.IsNullOrWhiteSpace(address)) { if (!Uri.TryCreate(address, UriKind.Absolute, out var absolute) && (JsonSchemaOptions.RefResolution == RefResolutionStrategy.ProcessSiblingId || context.Root.SupportedVersions == JsonSchemaVersion.Draft2019_09)) { address = context.Local.Id + address; } if (documentPath != null && !Uri.TryCreate(address, UriKind.Absolute, out absolute)) { var uriFolder = documentPath.OriginalString.EndsWith("/") ? documentPath : documentPath.GetParentUri(); absolute = new Uri(uriFolder, address); address = absolute.OriginalString; } _resolvedRoot = JsonSchemaRegistry.Get(address); } else { _resolvedRoot = context.Root; } if (_resolvedRoot == null) { Log.Schema("Could not resolve root of reference"); return; } var wellKnown = JsonSchemaRegistry.GetWellKnown(Reference); if (wellKnown != null) { Log.Schema("Well known reference found"); Resolved = wellKnown; return; } _ResolveLocalReference(_resolvedRoot?.DocumentPath ?? context.BaseUri !); }