/
JsonSchemaReferenceUtilities.cs
173 lines (154 loc) · 8.65 KB
/
JsonSchemaReferenceUtilities.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//-----------------------------------------------------------------------
// <copyright file="JsonSchemaReferenceUtilities.cs" company="NJsonSchema">
// Copyright (c) Rico Suter. All rights reserved.
// </copyright>
// <license>https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md</license>
// <author>Rico Suter, mail@rsuter.com</author>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Serialization;
using NJsonSchema.References;
using NJsonSchema.Visitors;
namespace NJsonSchema
{
/// <summary>Provides utilities to resolve and set JSON schema references.</summary>
public static class JsonSchemaReferenceUtilities
{
/// <summary>Updates all <see cref="IJsonReferenceBase.Reference"/> properties from the
/// available <see cref="IJsonReferenceBase.Reference"/> properties.</summary>
/// <param name="referenceResolver">The JSON document resolver.</param>
/// <param name="rootObject">The root object.</param>
public static Task UpdateSchemaReferencesAsync(object rootObject, JsonReferenceResolver referenceResolver) =>
UpdateSchemaReferencesAsync(rootObject, referenceResolver, new DefaultContractResolver());
/// <summary>Updates all <see cref="IJsonReferenceBase.Reference"/> properties from the
/// available <see cref="IJsonReferenceBase.Reference"/> properties.</summary>
/// <param name="referenceResolver">The JSON document resolver.</param>
/// <param name="rootObject">The root object.</param>
/// <param name="contractResolver">The contract resolver.</param>
public static async Task UpdateSchemaReferencesAsync(object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver)
{
var updater = new JsonReferenceUpdater(rootObject, referenceResolver, contractResolver);
await updater.VisitAsync(rootObject).ConfigureAwait(false);
}
/// <summary>Converts JSON references ($ref) to property references.</summary>
/// <param name="data">The data.</param>
/// <returns>The data.</returns>
public static string ConvertJsonReferences(string data)
{
return data.Replace("$ref", JsonPathUtilities.ReferenceReplaceString);
}
/// <summary>Converts property references to JSON references ($ref).</summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public static string ConvertPropertyReferences(string data)
{
return data.Replace(JsonPathUtilities.ReferenceReplaceString, "$ref");
}
/// <summary>Updates the <see cref="IJsonReferenceBase.Reference" /> properties
/// from the available <see cref="IJsonReferenceBase.Reference" /> properties with inlining external references.</summary>
/// <param name="rootObject">The root object.</param>
public static void UpdateSchemaReferencePaths(object rootObject)
{
UpdateSchemaReferencePaths(rootObject, false, new DefaultContractResolver());
}
/// <summary>Updates the <see cref="IJsonReferenceBase.Reference" /> properties
/// from the available <see cref="IJsonReferenceBase.Reference" /> properties.</summary>
/// <param name="rootObject">The root object.</param>
/// <param name="removeExternalReferences">Specifies whether to remove external references (otherwise they are inlined).</param>
/// <param name="contractResolver">The contract resolver.</param>
public static void UpdateSchemaReferencePaths(object rootObject, bool removeExternalReferences, IContractResolver contractResolver)
{
var schemaReferences = new Dictionary<IJsonReference, IJsonReference>();
var updater = new JsonReferencePathUpdater(rootObject, schemaReferences, removeExternalReferences, contractResolver);
updater.VisitAsync(rootObject).GetAwaiter().GetResult();
var searchedSchemas = schemaReferences.Select(p => p.Value).Distinct();
var result = JsonPathUtilities.GetJsonPaths(rootObject, searchedSchemas, contractResolver);
foreach (var p in schemaReferences)
p.Key.ReferencePath = result[p.Value];
}
private class JsonReferenceUpdater : JsonReferenceVisitorBase
{
private readonly object _rootObject;
private readonly JsonReferenceResolver _referenceResolver;
private bool _replaceRefsRound;
public JsonReferenceUpdater(object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver)
: base(contractResolver)
{
_rootObject = rootObject;
_referenceResolver = referenceResolver;
}
public override async Task VisitAsync(object obj)
{
_replaceRefsRound = true;
await base.VisitAsync(obj).ConfigureAwait(false);
_replaceRefsRound = false;
await base.VisitAsync(obj).ConfigureAwait(false);
}
protected override async Task<IJsonReference> VisitJsonReferenceAsync(IJsonReference reference, string path, string typeNameHint)
{
if (reference.ReferencePath != null && reference.Reference == null)
{
if (_replaceRefsRound)
{
if (path.EndsWith("/definitions/" + typeNameHint) || path.EndsWith("/schemas/" + typeNameHint))
{
// inline $refs in "definitions"
return await _referenceResolver
.ResolveReferenceWithoutAppendAsync(_rootObject, reference.ReferencePath)
.ConfigureAwait(false);
}
}
else
{
// load $refs and add them to "definitions"
reference.Reference = await _referenceResolver
.ResolveReferenceAsync(_rootObject, reference.ReferencePath)
.ConfigureAwait(false);
}
}
return reference;
}
}
private class JsonReferencePathUpdater : JsonReferenceVisitorBase
{
private readonly object _rootObject;
private readonly Dictionary<IJsonReference, IJsonReference> _schemaReferences;
private readonly bool _removeExternalReferences;
private readonly IContractResolver _contractResolver;
public JsonReferencePathUpdater(object rootObject, Dictionary<IJsonReference, IJsonReference> schemaReferences, bool removeExternalReferences, IContractResolver contractResolver)
: base(contractResolver)
{
_rootObject = rootObject;
_schemaReferences = schemaReferences;
_removeExternalReferences = removeExternalReferences;
_contractResolver = contractResolver;
}
#pragma warning disable 1998
protected override async Task<IJsonReference> VisitJsonReferenceAsync(IJsonReference reference, string path, string typeNameHint)
#pragma warning restore 1998
{
if (reference.Reference != null)
{
if (!_removeExternalReferences || reference.Reference.DocumentPath == null)
_schemaReferences[reference] = reference.Reference.ActualObject;
else
{
var externalReference = reference.Reference;
var externalReferenceRoot = externalReference.FindParentDocument();
reference.ReferencePath = externalReference.DocumentPath + JsonPathUtilities.GetJsonPath(
externalReferenceRoot, externalReference, _contractResolver).TrimEnd('#');
}
}
else if (_removeExternalReferences && _rootObject != reference && reference.DocumentPath != null)
{
throw new NotSupportedException("removeExternalReferences not supported");
//return new JsonSchema4 { ReferencePath = reference.DocumentPath };
}
return reference;
}
}
}
}