<PackageReference Include="NJsonSchema" Version="10.0.26" />

JsonReferenceResolver

public class JsonReferenceResolver
Resolves JSON Pointer references.
using Namotion.Reflection; using Newtonsoft.Json; using NJsonSchema.Infrastructure; using NJsonSchema.References; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace NJsonSchema { public class JsonReferenceResolver { private readonly JsonSchemaAppender _schemaAppender; private readonly Dictionary<string, IJsonReference> _resolvedObjects = new Dictionary<string, IJsonReference>(); public JsonReferenceResolver(JsonSchemaAppender schemaAppender) { _schemaAppender = schemaAppender; } public static Func<JsonSchema, JsonReferenceResolver> CreateJsonReferenceResolverFactory(ITypeNameGenerator typeNameGenerator) { return (JsonSchema schema) => new JsonReferenceResolver(new JsonSchemaAppender(schema, typeNameGenerator)); } public void AddDocumentReference(string documentPath, IJsonReference schema) { _resolvedObjects[documentPath.Contains("://") ? documentPath : DynamicApis.GetFullPath(documentPath)] = schema; } public async Task<IJsonReference> ResolveReferenceAsync(object rootObject, string jsonPath) { return await ResolveReferenceAsync(rootObject, jsonPath, true).ConfigureAwait(false); } public async Task<IJsonReference> ResolveReferenceWithoutAppendAsync(object rootObject, string jsonPath) { return await ResolveReferenceAsync(rootObject, jsonPath, false).ConfigureAwait(false); } public virtual IJsonReference ResolveDocumentReference(object rootObject, string jsonPath) { List<string> segments = jsonPath.Split(new char[1] { '/' }).Skip(1).ToList(); IJsonReference jsonReference = ResolveDocumentReference(rootObject, segments, new HashSet<object>()); if (jsonReference == null) throw new InvalidOperationException("Could not resolve the path '" + jsonPath + "'."); return jsonReference; } public virtual async Task<IJsonReference> ResolveFileReferenceAsync(string filePath) { return await JsonSchema.FromFileAsync(filePath, (JsonSchema schema) => this).ConfigureAwait(false); } public virtual async Task<IJsonReference> ResolveUrlReferenceAsync(string url) { return await JsonSchema.FromUrlAsync(url, (JsonSchema schema) => this).ConfigureAwait(false); } private async Task<IJsonReference> ResolveReferenceAsync(object rootObject, string jsonPath, bool append) { if (jsonPath == "#") { if (rootObject is IJsonReference) return (IJsonReference)rootObject; throw new InvalidOperationException("Could not resolve the JSON path '#' because the root object is not a JsonSchema4."); } if (jsonPath.StartsWith("#/")) return ResolveDocumentReference(rootObject, jsonPath); if (jsonPath.StartsWith("http://") || jsonPath.StartsWith("https://")) return await ResolveUrlReferenceWithAlreadyResolvedCheckAsync(jsonPath, jsonPath, append).ConfigureAwait(false); string text = (rootObject as IDocumentPathProvider)?.DocumentPath; if (text != null) { if (text.StartsWith("http://") || text.StartsWith("https://")) return await ResolveUrlReferenceWithAlreadyResolvedCheckAsync(new Uri(new Uri(text), jsonPath).ToString(), jsonPath, append).ConfigureAwait(false); return await ResolveFileReferenceWithAlreadyResolvedCheckAsync(DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(text), jsonPath), jsonPath, append).ConfigureAwait(false); } throw new NotSupportedException("Could not resolve the JSON path '" + jsonPath + "' because no document path is available."); } private async Task<IJsonReference> ResolveFileReferenceWithAlreadyResolvedCheckAsync(string fullJsonPath, string jsonPath, bool append) { try { string[] arr = Regex.Split(fullJsonPath, "(?=#)"); string filePath = DynamicApis.GetFullPath(arr[0]); if (!_resolvedObjects.ContainsKey(filePath)) { IJsonReference jsonReference = await ResolveFileReferenceAsync(filePath).ConfigureAwait(false); jsonReference.DocumentPath = jsonPath; _resolvedObjects[filePath] = jsonReference; } IJsonReference referencedFile = _resolvedObjects[filePath]; IJsonReference jsonReference2 = (arr.Length != 1) ? (await ResolveReferenceAsync(referencedFile, arr[1]).ConfigureAwait(false)) : referencedFile; if ((jsonReference2 is JsonSchema) & append) { JsonSchema obj = _schemaAppender.RootObject as JsonSchema; if (obj == null || !obj.Definitions.Values.Contains(referencedFile)) { string typeNameHint = jsonPath.Split('/', '\\').Last().Split(new char[1] { '.' }) .First(); _schemaAppender.AppendSchema((JsonSchema)jsonReference2, typeNameHint); } } return jsonReference2; } catch (Exception innerException) { throw new InvalidOperationException("Could not resolve the JSON path '" + jsonPath + "' with the full JSON path '" + fullJsonPath + "'.", innerException); } } private async Task<IJsonReference> ResolveUrlReferenceWithAlreadyResolvedCheckAsync(string fullJsonPath, string jsonPath, bool append) { try { string[] arr = fullJsonPath.Split(new char[1] { '#' }); if (!_resolvedObjects.ContainsKey(arr[0])) { IJsonReference jsonReference = await ResolveUrlReferenceAsync(arr[0]).ConfigureAwait(false); jsonReference.DocumentPath = jsonPath; if ((jsonReference is JsonSchema) & append) _schemaAppender.AppendSchema((JsonSchema)jsonReference, null); _resolvedObjects[arr[0]] = jsonReference; } IJsonReference jsonReference2 = _resolvedObjects[arr[0]]; return (arr.Length != 1) ? (await ResolveReferenceAsync(jsonReference2, "#" + arr[1]).ConfigureAwait(false)) : jsonReference2; } catch (Exception innerException) { throw new InvalidOperationException("Could not resolve the JSON path '" + jsonPath + "' with the full JSON path '" + fullJsonPath + "'.", innerException); } } private IJsonReference ResolveDocumentReference(object obj, List<string> segments, HashSet<object> checkedObjects) { if (obj == null || obj is string || checkedObjects.Contains(obj)) return null; IJsonReference jsonReference = obj as IJsonReference; if (jsonReference != null && jsonReference.Reference != null) { IJsonReference jsonReference2 = ResolveDocumentReferenceWithoutDereferencing(jsonReference.Reference, segments, checkedObjects); if (jsonReference2 == null) return ResolveDocumentReferenceWithoutDereferencing(obj, segments, checkedObjects); return jsonReference2; } return ResolveDocumentReferenceWithoutDereferencing(obj, segments, checkedObjects); } private IJsonReference ResolveDocumentReferenceWithoutDereferencing(object obj, List<string> segments, HashSet<object> checkedObjects) { if (segments.Count == 0) return obj as IJsonReference; checkedObjects.Add(obj); string text = segments[0]; if (obj is IDictionary) { if (((IDictionary)obj).Contains(text)) return ResolveDocumentReference(((IDictionary)obj)[text], segments.Skip(1).ToList(), checkedObjects); } else if (obj is IEnumerable) { if (int.TryParse(text, out int result)) { object[] array = ((IEnumerable)obj).Cast<object>().ToArray(); if (array.Length > result) return ResolveDocumentReference(array[result], segments.Skip(1).ToList(), checkedObjects); } } else { IJsonExtensionObject jsonExtensionObject = obj as IJsonExtensionObject; if (jsonExtensionObject != null) { bool? nullable = jsonExtensionObject.ExtensionData?.ContainsKey(text); bool flag = true; if ((nullable.GetValueOrDefault() == flag) & nullable.HasValue) return ResolveDocumentReference(jsonExtensionObject.ExtensionData[text], segments.Skip(1).ToList(), checkedObjects); } foreach (ContextualMemberInfo item in from p in ContextualTypeExtensions.GetContextualPropertiesAndFields(obj.GetType()) where p.GetTypeAttribute<JsonIgnoreAttribute>() == null select p) { if (item.GetName() == text) { object value = item.GetValue(obj); return ResolveDocumentReference(value, segments.Skip(1).ToList(), checkedObjects); } } } return null; } } }