Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/NJsonSchema/JsonSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ namespace NJsonSchema
public partial class JsonSchema : IDocumentPathProvider
{
internal static readonly HashSet<string> JsonSchemaPropertiesCache =
[..typeof(JsonSchema).GetContextualProperties().Select(p => p.Name).ToArray()];
[
..typeof(JsonSchema).GetContextualProperties().Select(p => p.Name)
];

private const SchemaType SerializationSchemaType = SchemaType.JsonSchema;

Expand Down
35 changes: 21 additions & 14 deletions src/NJsonSchema/Visitors/AsyncJsonReferenceVisitorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ public virtual async Task VisitAsync(object obj, CancellationToken cancellationT
protected virtual async Task VisitAsync(object obj, string path, string? typeNameHint, ISet<object> checkedObjects, Action<object> replacer, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (obj == null || checkedObjects.Contains(obj))

if (obj == null || obj is string || !checkedObjects.Add(obj))
{
return;
}

checkedObjects.Add(obj);

if (obj is IJsonReference reference)
{
var newReference = await VisitJsonReferenceAsync(reference, path, typeNameHint, cancellationToken).ConfigureAwait(false);
Expand Down Expand Up @@ -174,21 +173,29 @@ await VisitAsync(p.Value, path + "/definitions/" + p.Key, p.Key, checkedObjects,
}
}

if (obj is not string && obj is not JToken && obj.GetType() != typeof(JsonSchema)) // Reflection fallback
if (obj is not JToken && obj.GetType() != typeof(JsonSchema)) // Reflection fallback
{
var pathPrefix = path + "/";
if (_contractResolver.ResolveContract(obj.GetType()) is JsonObjectContract contract)
{
foreach (var property in contract.Properties.Where(p =>
{
bool isJsonSchemaProperty = obj is JsonSchema && p.UnderlyingName != null && JsonSchema.JsonSchemaPropertiesCache.Contains(p.UnderlyingName);
return !isJsonSchemaProperty && !p.Ignored &&
p.ShouldSerialize?.Invoke(obj) != false;
}))
foreach (var p in contract.Properties)
{
var value = property.ValueProvider?.GetValue(obj);
var isJsonSchemaProperty = obj is JsonSchema && p.UnderlyingName != null && JsonSchema.JsonSchemaPropertiesCache.Contains(p.UnderlyingName);
if (isJsonSchemaProperty
|| p.Ignored
|| p.PropertyType == typeof(string)
|| p.PropertyType?.IsPrimitive == true
|| p.ShouldSerialize?.Invoke(obj) == false)
{
continue;
}

var value = p.ValueProvider?.GetValue(obj);
if (value != null)
{
await VisitAsync(value, path + "/" + property.PropertyName, property.PropertyName, checkedObjects, o => property.ValueProvider?.SetValue(obj, o), cancellationToken).ConfigureAwait(false);
// to avoid closure allocations
var temp = p.ValueProvider;
await VisitAsync(value, pathPrefix + p.PropertyName, p.PropertyName, checkedObjects, o => temp?.SetValue(obj, o), cancellationToken).ConfigureAwait(false);
}
}
}
Expand All @@ -199,7 +206,7 @@ await VisitAsync(p.Value, path + "/definitions/" + p.Key, p.Key, checkedObjects,
var value = dictionary[key];
if (value != null)
{
await VisitAsync(value, path + "/" + key, key.ToString(), checkedObjects, o =>
await VisitAsync(value, pathPrefix + key, key.ToString(), checkedObjects, o =>
{
if (o != null)
{
Expand All @@ -224,7 +231,7 @@ await VisitAsync(value, path + "/" + key, key.ToString(), checkedObjects, o =>
var value = property.GetValue(obj);
if (value != null)
{
await VisitAsync(value, path + "/" + property.Name, property.Name, checkedObjects, o => property.SetValue(obj, o), cancellationToken).ConfigureAwait(false);
await VisitAsync(value, pathPrefix + property.Name, property.Name, checkedObjects, o => property.SetValue(obj, o), cancellationToken).ConfigureAwait(false);
}
}
}
Expand Down