Skip to content

Commit

Permalink
Small performance improvements (#1636)
Browse files Browse the repository at this point in the history
* change IReflectionService to return reusable fun to generate enum values via GetEnumValueConverter
* fix inheritdoc typo
* use string interpolation and less LINQ in JsonReferenceVisitorBase
  • Loading branch information
lahma committed Oct 30, 2023
1 parent a1f49d7 commit 855dab7
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@
using NJsonSchema.Infrastructure;
using System.Runtime.Serialization;
using System.Reflection;
using NJsonSchema;
using NJsonSchema.Generation;
using System.Collections.Generic;

namespace NJsonSchema.NewtonsoftJson.Generation
{
/// <inheritdocs />
/// <inheritdoc />
public class NewtonsoftJsonReflectionService : ReflectionServiceBase<NewtonsoftJsonSchemaGeneratorSettings>
{
/// <inheritdocs />
/// <inheritdoc />
protected override JsonTypeDescription GetDescription(ContextualType contextualType, NewtonsoftJsonSchemaGeneratorSettings settings,
Type originalType, bool isNullable, ReferenceTypeNullHandling defaultReferenceTypeNullHandling)
{
Expand All @@ -38,7 +36,7 @@ public class NewtonsoftJsonReflectionService : ReflectionServiceBase<NewtonsoftJ
return base.GetDescription(contextualType, settings, originalType, isNullable, defaultReferenceTypeNullHandling);
}

/// <inheritdocs />
/// <inheritdoc />
public override bool IsNullable(ContextualType contextualType, ReferenceTypeNullHandling defaultReferenceTypeNullHandling)
{
var jsonPropertyAttribute = contextualType.GetContextAttribute<JsonPropertyAttribute>();
Expand All @@ -50,28 +48,27 @@ public override bool IsNullable(ContextualType contextualType, ReferenceTypeNull
return base.IsNullable(contextualType, defaultReferenceTypeNullHandling);
}

/// <inheritdocs />
/// <inheritdoc />
public override bool IsStringEnum(ContextualType contextualType, NewtonsoftJsonSchemaGeneratorSettings settings)
{
var hasGlobalStringEnumConverter = settings.SerializerSettings?.Converters.OfType<StringEnumConverter>().Any() == true;
return hasGlobalStringEnumConverter || base.IsStringEnum(contextualType, settings);
}

/// <inheritdocs />
public override string ConvertEnumValue(object value, NewtonsoftJsonSchemaGeneratorSettings settings)

/// <inheritdoc />
public override Func<object, string?> GetEnumValueConverter(NewtonsoftJsonSchemaGeneratorSettings settings)
{
var converters = settings.SerializerSettings?.Converters.ToList() ?? new List<JsonConverter>();
var converters = settings.SerializerSettings.Converters.ToList();
if (!converters.OfType<StringEnumConverter>().Any())
{
converters.Add(new StringEnumConverter());
}

var json = JsonConvert.SerializeObject(value, Formatting.None, converters.ToArray());
var enumString = JsonConvert.DeserializeObject<string>(json)!;
return enumString;
return x => JsonConvert.DeserializeObject<string?>(JsonConvert.SerializeObject(x, Formatting.None, converters.ToArray()));
}

/// <inheritdocs />
/// <inheritdoc />
public override void GenerateProperties(JsonSchema schema, ContextualType contextualType, NewtonsoftJsonSchemaGeneratorSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver)
{
var contextualAccessors = contextualType
Expand Down Expand Up @@ -150,7 +147,7 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex
}
}

/// <inheritdocs />
/// <inheritdoc />
public override string GetPropertyName(ContextualAccessorInfo accessorInfo, JsonSchemaGeneratorSettings settings)
{
return GetPropertyName(null, accessorInfo, (NewtonsoftJsonSchemaGeneratorSettings)settings);
Expand Down Expand Up @@ -197,7 +194,7 @@ private void LoadPropertyOrField(JsonProperty jsonProperty, ContextualAccessorIn
}
}

private string GetPropertyName(JsonProperty? jsonProperty, ContextualAccessorInfo accessorInfo, NewtonsoftJsonSchemaGeneratorSettings settings)
private static string GetPropertyName(JsonProperty? jsonProperty, ContextualAccessorInfo accessorInfo, NewtonsoftJsonSchemaGeneratorSettings settings)
{
if (jsonProperty?.PropertyName != null)
{
Expand All @@ -208,8 +205,7 @@ private string GetPropertyName(JsonProperty? jsonProperty, ContextualAccessorInf
{
var propertyName = accessorInfo.GetName();

var contractResolver = settings.ActualContractResolver as DefaultContractResolver;
return contractResolver != null
return settings.ActualContractResolver is DefaultContractResolver contractResolver
? contractResolver.GetResolvedPropertyName(propertyName)
: propertyName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace NJsonSchema.NewtonsoftJson.Generation
{
/// <inheritdocs />
/// <inheritdoc />
public class NewtonsoftJsonSchemaGeneratorSettings : JsonSchemaGeneratorSettings
{
private Dictionary<string, JsonContract?> _cachedContracts = new Dictionary<string, JsonContract?>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public JsonSchemaExtensionDataAttribute(string key, object value)
/// <summary>Gets the value.</summary>
public object Value { get; }

/// <inheritdocs />
/// <inheritdoc />
public IReadOnlyDictionary<string, object> ExtensionData => new Dictionary<string, object>
{
{ Key, Value }
Expand Down
5 changes: 2 additions & 3 deletions src/NJsonSchema/Generation/IReflectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ namespace NJsonSchema.Generation
public interface IReflectionService
{
/// <summary>
/// Converts an enum value to a JSON string.
/// Get converter that converts an enum value to a JSON string.
/// </summary>
/// <param name="value"></param>
/// <param name="settings"></param>
/// <returns></returns>
string ConvertEnumValue(object value, JsonSchemaGeneratorSettings settings);
Func<object, string?> GetEnumValueConverter(JsonSchemaGeneratorSettings settings);

/// <summary>
/// Gets the property name for the given accessor info.
Expand Down
12 changes: 7 additions & 5 deletions src/NJsonSchema/Generation/JsonSchemaGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;

namespace NJsonSchema.Generation
{
Expand Down Expand Up @@ -700,6 +701,7 @@ protected virtual void GenerateEnum(JsonSchema schema, JsonTypeDescription typeD
schema.EnumerationNames.Clear();
schema.IsFlagEnumerable = contextualType.GetInheritedAttribute<FlagsAttribute>() != null;

Func<object, string?>? enumValueConverter = null;
var underlyingType = Enum.GetUnderlyingType(contextualType.Type);
foreach (var enumName in Enum.GetNames(contextualType.Type))
{
Expand All @@ -711,16 +713,16 @@ protected virtual void GenerateEnum(JsonSchema schema, JsonTypeDescription typeD
else
{
// EnumMember only checked if StringEnumConverter is used
var attributes = contextualType.Type.GetRuntimeField(enumName)?.GetCustomAttributes();
dynamic? enumMemberAttribute = attributes?.FirstAssignableToTypeNameOrDefault("System.Runtime.Serialization.EnumMemberAttribute");
if (!string.IsNullOrEmpty(enumMemberAttribute?.Value))
var enumMemberAttribute = contextualType.Type.GetRuntimeField(enumName)?.GetCustomAttribute<EnumMemberAttribute>();
if (enumMemberAttribute != null && !string.IsNullOrEmpty(enumMemberAttribute.Value))
{
schema.Enumeration.Add((string)enumMemberAttribute!.Value);
schema.Enumeration.Add(enumMemberAttribute.Value);
}
else
{
enumValueConverter ??= Settings.ReflectionService.GetEnumValueConverter(Settings);
var value = Enum.Parse(contextualType.Type, enumName);
schema.Enumeration.Add(Settings.ReflectionService.ConvertEnumValue(value, Settings));
schema.Enumeration.Add(enumValueConverter(value));
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/NJsonSchema/Generation/ReflectionServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ namespace NJsonSchema.Generation
public abstract class ReflectionServiceBase<TSettings> : IReflectionService
where TSettings : JsonSchemaGeneratorSettings
{
/// <inheritdocs />
public abstract string ConvertEnumValue(object value, TSettings settings);
/// <inheritdoc />
public abstract Func<object, string?> GetEnumValueConverter(TSettings settings);

/// <inheritdocs />
/// <inheritdoc />
public abstract void GenerateProperties(JsonSchema schema, ContextualType contextualType, TSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver);

/// <inheritdocs />
/// <inheritdoc />
public abstract string GetPropertyName(ContextualAccessorInfo accessorInfo, JsonSchemaGeneratorSettings settings);

/// <summary>Creates a <see cref="JsonTypeDescription"/> from a <see cref="Type"/>. </summary>
Expand Down Expand Up @@ -368,9 +368,9 @@ bool IReflectionService.IsStringEnum(ContextualType contextualType, JsonSchemaGe
return IsStringEnum(contextualType, (TSettings)settings);
}

string IReflectionService.ConvertEnumValue(object value, JsonSchemaGeneratorSettings settings)
Func<object, string?> IReflectionService.GetEnumValueConverter(JsonSchemaGeneratorSettings settings)
{
return ConvertEnumValue(value, (TSettings)settings);
return GetEnumValueConverter((TSettings)settings);
}

void IReflectionService.GenerateProperties(JsonSchema schema, ContextualType contextualType, JsonSchemaGeneratorSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver)
Expand Down
32 changes: 13 additions & 19 deletions src/NJsonSchema/Generation/SystemTextJsonReflectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@

namespace NJsonSchema.Generation
{
/// <inheritdocs />
/// <inheritdoc />
public class SystemTextJsonReflectionService : ReflectionServiceBase<SystemTextJsonSchemaGeneratorSettings>
{
/// <inheritdocs />
/// <inheritdoc />
public override void GenerateProperties(JsonSchema schema, ContextualType contextualType, SystemTextJsonSchemaGeneratorSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver)
{
foreach (var accessorInfo in contextualType
.Properties
.OfType<ContextualAccessorInfo>()
.Concat(contextualType.Fields)
.Where(p => p.MemberInfo.DeclaringType == contextualType.Type))
foreach (var accessorInfo in contextualType.Properties.OfType<ContextualAccessorInfo>().Concat(contextualType.Fields))
{
if (accessorInfo.MemberInfo is FieldInfo fieldInfo &&
(fieldInfo.IsPrivate || fieldInfo.IsStatic || !fieldInfo.IsDefined(typeof(DataMemberAttribute))))
if (accessorInfo.MemberInfo.DeclaringType != contextualType.Type ||
(accessorInfo.MemberInfo is FieldInfo fieldInfo && (fieldInfo.IsPrivate || fieldInfo.IsStatic || !fieldInfo.IsDefined(typeof(DataMemberAttribute)))))
{
continue;
}
Expand Down Expand Up @@ -70,7 +66,7 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex

if (!ignored)
{
var propertyTypeDescription = ((IReflectionService)this).GetDescription(accessorInfo.AccessorType, settings);
var propertyTypeDescription = GetDescription(accessorInfo.AccessorType, settings.DefaultReferenceTypeNullHandling, settings);
var propertyName = GetPropertyName(accessorInfo, settings);

var propertyAlreadyExists = schema.Properties.ContainsKey(propertyName);
Expand All @@ -82,7 +78,7 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex
}
else
{
throw new InvalidOperationException("The JSON property '" + propertyName + "' is defined multiple times on type '" + contextualType.Type.FullName + "'.");
throw new InvalidOperationException($"The JSON property '{propertyName}' is defined multiple times on type '{contextualType.Type.FullName}'.");
}
}

Expand All @@ -104,17 +100,16 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex
}
}

/// <inheritdocs />
/// <inheritdoc />
public override bool IsStringEnum(ContextualType contextualType, SystemTextJsonSchemaGeneratorSettings settings)
{
var hasGlobalStringEnumConverter = settings.SerializerOptions.Converters.OfType<JsonStringEnumConverter>().Any();
return hasGlobalStringEnumConverter || base.IsStringEnum(contextualType, settings);
}

/// <inheritdocs />
public override string ConvertEnumValue(object value, SystemTextJsonSchemaGeneratorSettings settings)
/// <inheritdoc />
public override Func<object, string?> GetEnumValueConverter(SystemTextJsonSchemaGeneratorSettings settings)
{
// TODO(performance): How to improve this one here?
var serializerOptions = new JsonSerializerOptions();
foreach (var converter in settings.SerializerOptions.Converters)
{
Expand All @@ -126,17 +121,16 @@ public override string ConvertEnumValue(object value, SystemTextJsonSchemaGenera
serializerOptions.Converters.Add(new JsonStringEnumConverter());
}

var json = JsonSerializer.Serialize(value, value.GetType(), serializerOptions);
return JsonSerializer.Deserialize<string>(json)!;
return x => JsonSerializer.Deserialize<string?>(JsonSerializer.Serialize(x, x.GetType(), serializerOptions));
}

/// <inheritdocs />
/// <inheritdoc />
public override string GetPropertyName(ContextualAccessorInfo accessorInfo, JsonSchemaGeneratorSettings settings)
{
return GetPropertyName(accessorInfo, (SystemTextJsonSchemaGeneratorSettings)settings);
}

private string GetPropertyName(ContextualAccessorInfo accessorInfo, SystemTextJsonSchemaGeneratorSettings settings)
private static string GetPropertyName(ContextualAccessorInfo accessorInfo, SystemTextJsonSchemaGeneratorSettings settings)
{
dynamic? jsonPropertyNameAttribute = accessorInfo.ContextAttributes
.FirstAssignableToTypeNameOrDefault("System.Text.Json.Serialization.JsonPropertyNameAttribute", TypeNameStyle.FullName);
Expand Down
2 changes: 1 addition & 1 deletion src/NJsonSchema/JsonPathUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static class JsonPathUtilities
if (searchedObjects.ContainsKey(obj))
{
searchedObjects[obj] = basePath;
if (searchedObjects.All(p => p.Value != null))
if (searchedObjects.All(static p => p.Value != null))
{
return true;
}
Expand Down

0 comments on commit 855dab7

Please sign in to comment.