private static void PatchForObject(JsonElement orig, JsonElement mod, List <PatchOperation> patch, JsonPointer path) { var origNames = orig.EnumerateObject().Select(x => x.Name).ToArray(); var modNames = mod.EnumerateObject().Select(x => x.Name).ToArray(); foreach (var k in origNames.Except(modNames)) { patch.Add(PatchOperation.Remove(path.Combine(k))); } foreach (var k in modNames.Except(origNames)) { var prop = mod.EnumerateObject().First(p => p.NameEquals(k)); patch.Add(PatchOperation.Add(path.Combine(k), prop.Value)); } foreach (var k in origNames.Intersect(modNames)) { var origProp = orig.EnumerateObject().First(p => p.NameEquals(k)); var modProp = mod.EnumerateObject().First(p => p.NameEquals(k)); if (origProp.Value.ValueKind != modProp.Value.ValueKind) { patch.Add(PatchOperation.Replace(JsonPointer.Parse(path + modProp.Name), modProp.Value)); } else if (!string.Equals(origProp.Value.ToString(), modProp.Value.ToString())) // TODO { if (origProp.Value.ValueKind == JsonValueKind.Object) { PatchForObject(origProp.Value, modProp.Value, patch, path.Combine(modProp.Name)); } else if (origProp.Value.ValueKind == JsonValueKind.Array) { PatchForArray(origProp.Value, modProp.Value, patch, path.Combine(modProp.Name)); } else { patch.Add(PatchOperation.Replace(path.Combine(modProp.Name), modProp.Value)); } } } }
private void ProcessRefKeyword(JsonPointer path, RefKeyword keyword, SchemaContext context) { int subSchemaIndex = 0; foreach (var subSchema in keyword.GetSubschemas()) { var subSchemaPath = path.Combine(JsonPointer.Parse($"/[{subSchemaIndex}]")); ProcessSubSchema(subSchemaPath, subSchema, context); subSchemaIndex++; } }
/// <summary> /// Recursively analyzes all schemas for the provided keyword. /// </summary> /// <param name="path"><see cref="JsonPointer"/> representing the actual path to the keyword being provided.</param> /// <param name="keyword">The keyword to be analyzed.</param> protected void AnalyzeKeyword(JsonPointer path, IJsonSchemaKeyword keyword) { switch (keyword) { case AllOfKeyword item: for (var i = 0; i < item.Schemas.Count; i++) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/[{i}]")), item.Schemas[i]); } break; case AnyOfKeyword item: for (var i = 0; i < item.Schemas.Count; i++) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/[{i}]")), item.Schemas[i]); } break; case OneOfKeyword item: for (var i = 0; i < item.Schemas.Count; i++) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/[{i}]")), item.Schemas[i]); } break; case DefinitionsKeyword item: foreach (var(name, definition) in item.Definitions) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/{name}")), definition); } break; case DefsKeyword item: foreach (var(name, definition) in item.Definitions) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/{name}")), definition); } break; case PropertiesKeyword item: foreach (var(name, definition) in item.Properties) { AnalyzeSchema(path.Combine(JsonPointer.Parse($"/{name}")), definition); } break; case ISchemaContainer schemaContainer: AnalyzeSchema(path, schemaContainer.Schema); break; } }
private void ProcessOneOfKeyword(JsonPointer path, OneOfKeyword keyword, SchemaContext context) { // A oneOf keyword with only one subschema which isn't null makes it required if (KeywordHasSingleNonNullSchema(keyword)) { AddRequiredProperties(context.Id, new List <string>() { context.Name }); } int subSchemaIndex = 0; foreach (var subSchema in keyword.GetSubschemas()) { var subSchemaPath = path.Combine(JsonPointer.Parse($"/[{subSchemaIndex}]")); ProcessSubSchema(subSchemaPath, subSchema, context); subSchemaIndex++; } }
private static void PatchForArray(JsonElement orig, JsonElement mod, List <PatchOperation> patch, JsonPointer path) { for (int i = 0; i < Math.Max(orig.GetArrayLength(), mod.GetArrayLength()); i++) { var ui = (uint)i; if (i >= orig.GetArrayLength()) { patch.Add(PatchOperation.Add(path.Combine(ui), mod[i])); continue; } if (i >= mod.GetArrayLength()) { patch.Add(PatchOperation.Remove(path.Combine(ui))); continue; } var origObject = orig[i]; var modObject = mod[i]; if (origObject.ValueKind != modObject.ValueKind) { patch.Add(PatchOperation.Replace(path.Combine("/" + i), modObject)); } else if (!string.Equals(origObject.ToString(), modObject.ToString())) // TODO { if (origObject.ValueKind == JsonValueKind.Object) { PatchForObject(origObject, modObject, patch, path.Combine(ui)); } else if (origObject.ValueKind == JsonValueKind.Array) { PatchForArray(origObject, modObject, patch, path.Combine(ui)); } else { patch.Add(PatchOperation.Replace(path.Combine(ui), modObject)); } } } }
/// <summary> /// Primary method to call for analyzing a Json Schema. /// </summary> /// <param name="path">The path to start analyzing. Normally this should be the root path when calling this method ie. '#'</param> /// <param name="schema">The Json Schema to analyze.</param> protected void AnalyzeSchema(JsonPointer path, JsonSchema schema) { if (TryParseAsNillableElement(schema, out var valueSchema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.Nillable); if (valueSchema != null) { AnalyzeSchema(path, valueSchema); return; } } if (TryParseAsArray(schema, out var itemSchema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.Array); AnalyzeSchema(path, itemSchema); return; } // Follow all references, this will mark the schema as the type referenced if it has a $ref keyword // This will analyze some schemas multiple times and can be optimized if needed schema = FollowReferencesIfAny(schema); if (IsValidSimpleType(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.SimpleType); if (IsValidAttribute(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.Attribute); } if (IsValidUnhandledAttribute(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.UnhandledAttribute); } } if (IsValidSimpleTypeRestriction(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.SimpleType); Metadata.AddCompatibleTypes(path, CompatibleXsdType.SimpleTypeRestriction); } if (IsValidComplexType(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.ComplexType); } if (IsValidSimpleContentExtension(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.SimpleContentExtension); } if (IsValidSimpleContentRestriction(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.SimpleContentRestriction); } if (IsValidComplexContentExtension(path, schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.ComplexContent); Metadata.AddCompatibleTypes(path, CompatibleXsdType.ComplexContentExtension); } if (schema.Keywords != null) { foreach (var keyword in schema.Keywords) { var keywordPath = path.Combine(JsonPointer.Parse($"/{keyword.Keyword()}")); AnalyzeKeyword(keywordPath, keyword); } } if (IsValidUnhandledAttribute(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.UnhandledAttribute); } if (IsValidUnhandledEnumAttribute(schema)) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.UnhandledEnumAttribute); } // Add "unknown" if no other was added on this path if (Metadata.GetCompatibleTypes(path).Count == 0) { Metadata.AddCompatibleTypes(path, CompatibleXsdType.Unknown); } }