diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs index 43a9c04d32..077578413b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs @@ -29,7 +29,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Json options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); - options.JsonSerializerOptions.Converters.Add(new AbpHasExtraPropertiesJsonConverterFactory()); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs index 7378ab8eff..9e8ed9f3fc 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs @@ -19,11 +19,17 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters return JsonSerializer.Serialize(d); } + private static readonly JsonSerializerOptions DeserializeOptions = new JsonSerializerOptions() + { + Converters = + { + new ObjectToInferredTypesConverter() + } + }; + private static TPropertyType DeserializeObject(string s) { - var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); - return JsonSerializer.Deserialize(s, deserializeOptions); + return JsonSerializer.Deserialize(s, DeserializeOptions); } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs index 2949437ad0..9db5624ebf 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs @@ -41,6 +41,14 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters return JsonSerializer.Serialize(copyDictionary); } + private static readonly JsonSerializerOptions DeserializeOptions = new JsonSerializerOptions() + { + Converters = + { + new ObjectToInferredTypesConverter() + } + }; + private static ExtraPropertyDictionary DeserializeObject(string extraPropertiesAsJson, Type entityType) { if (extraPropertiesAsJson.IsNullOrEmpty() || extraPropertiesAsJson == "{}") @@ -48,10 +56,7 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters return new ExtraPropertyDictionary(); } - var deserializeOptions = new JsonSerializerOptions(); - deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter()); - - var dictionary = JsonSerializer.Deserialize(extraPropertiesAsJson, deserializeOptions) ?? + var dictionary = JsonSerializer.Deserialize(extraPropertiesAsJson, DeserializeOptions) ?? new ExtraPropertyDictionary(); if (entityType != null) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs index cce91cb5d7..4d9d6ff4ff 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs @@ -90,6 +90,11 @@ namespace Volo.Abp.Http.Client.DynamicProxying return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(client, baseUrl)); } + private static readonly JsonSerializerOptions DeserializeOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + protected virtual async Task GetApiDescriptionFromServerAsync( HttpClient client, string baseUrl) @@ -113,10 +118,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying var content = await response.Content.ReadAsStringAsync(); - var result = JsonSerializer.Deserialize(content, new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }); + var result = JsonSerializer.Deserialize(content, DeserializeOptions); return result; } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs index 3c6357c92e..98b7fdbcb1 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs @@ -25,7 +25,6 @@ namespace Volo.Abp.Json.SystemTextJson options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); - options.JsonSerializerOptions.Converters.Add(new AbpHasExtraPropertiesJsonConverterFactory()); // If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters. options.JsonSerializerOptions.Encoder ??= JavaScriptEncoder.UnsafeRelaxedJsonEscaping; diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs index 570d1c162c..e136de347c 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Text.Json; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -39,21 +40,26 @@ namespace Volo.Abp.Json.SystemTextJson return JsonSerializer.Deserialize(jsonString, type, CreateJsonSerializerOptions(camelCase)); } + private readonly ConcurrentDictionary JsonSerializerOptionsCache = new ConcurrentDictionary(); + protected virtual JsonSerializerOptions CreateJsonSerializerOptions(bool camelCase = true, bool indented = false) { - var settings = new JsonSerializerOptions(Options.JsonSerializerOptions); - - if (camelCase) + return JsonSerializerOptionsCache.GetOrAdd($"default{camelCase}{indented}", _ => { - settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - } + var settings = new JsonSerializerOptions(Options.JsonSerializerOptions); - if (indented) - { - settings.WriteIndented = true; - } + if (camelCase) + { + settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + } + + if (indented) + { + settings.WriteIndented = true; + } - return settings; + return settings; + }); } } } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs deleted file mode 100644 index 792b96144e..0000000000 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Serialization; -using Volo.Abp.Data; - -namespace Volo.Abp.Json.SystemTextJson.JsonConverters -{ - public class AbpHasExtraPropertiesJsonConverter : JsonConverter - where T : IHasExtraProperties - { - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var newOptions = JsonSerializerOptionsHelper.Create(options, x => x == this); - - var converterFactory = newOptions.Converters.FirstOrDefault(x => x is AbpHasExtraPropertiesJsonConverterFactory).As(); - var newConverterFactory = new AbpHasExtraPropertiesJsonConverterFactory(); - if (converterFactory != null) - { - newOptions.Converters.Remove(converterFactory); - newConverterFactory.AddExcludeTypes(converterFactory.GetExcludeTypes().ToArray()); - } - - newConverterFactory.AddExcludeTypes(typeToConvert); - newOptions.Converters.Add(newConverterFactory); - - var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - if (rootElement.ValueKind == JsonValueKind.Object) - { - var extensibleObject = rootElement.Deserialize(newOptions); - - var extraPropertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)); - - if (extraPropertiesJsonProperty.Value.ValueKind == JsonValueKind.Object) - { - var extraPropertyDictionary = extraPropertiesJsonProperty.Value.Deserialize(typeof(ExtraPropertyDictionary), newOptions); - ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); - } - - return extensibleObject; - } - - throw new JsonException("RootElement's ValueKind is not Object!"); - } - - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) - { - var newOptions = JsonSerializerOptionsHelper.Create(options, x => - x == this || - x.GetType() == typeof(AbpHasExtraPropertiesJsonConverterFactory)); - - JsonSerializer.Serialize(writer, value, newOptions); - } - } -} diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverterFactory.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverterFactory.cs deleted file mode 100644 index dff25e00df..0000000000 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverterFactory.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; -using Volo.Abp.Data; - -namespace Volo.Abp.Json.SystemTextJson.JsonConverters -{ - public class AbpHasExtraPropertiesJsonConverterFactory : JsonConverterFactory - { - private static readonly ConcurrentDictionary CachedTypes = new ConcurrentDictionary(); - - private readonly List _excludeTypes = new List(); - - public virtual AbpHasExtraPropertiesJsonConverterFactory AddExcludeTypes(params Type[] excludeTypes) - { - _excludeTypes.AddIfNotContains(excludeTypes); - return this; - } - - public virtual IReadOnlyList GetExcludeTypes() - { - return _excludeTypes.ToImmutableList(); - } - - public override bool CanConvert(Type typeToConvert) - { - if (_excludeTypes.Contains(typeToConvert)) - { - return false; - } - - //Only for private or protected ExtraProperties. - if (typeof(IHasExtraProperties).IsAssignableFrom(typeToConvert)) - { - return CachedTypes.GetOrAdd(typeToConvert, type => - { - var property = type.GetProperty(nameof(IHasExtraProperties.ExtraProperties)); - if (property != null) - { - var setMethod = property.GetSetMethod(true); - return setMethod != null && (setMethod.IsPrivate || setMethod.IsFamily); - } - - return false; - }); - } - - return false; - } - - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - return (JsonConverter) Activator.CreateInstance( - typeof(AbpHasExtraPropertiesJsonConverter<>).MakeGenericType(typeToConvert), - BindingFlags.Instance | BindingFlags.Public, - binder: null, - null, - culture: null)!; - } - } -} diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToBooleanConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToBooleanConverter.cs index cfd5c17653..8469f3a3ae 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToBooleanConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToBooleanConverter.cs @@ -8,6 +8,8 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters { public class AbpStringToBooleanConverter : JsonConverter { + private JsonSerializerOptions _writeJsonSerializerOptions; + public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.String) @@ -29,9 +31,10 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - var entityConverter = (JsonConverter)newOptions.GetConverter(typeof(bool)); - entityConverter.Write(writer, value, newOptions); + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); + var entityConverter = (JsonConverter)_writeJsonSerializerOptions.GetConverter(typeof(bool)); + + entityConverter.Write(writer, value, _writeJsonSerializerOptions); } } } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToEnumConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToEnumConverter.cs index 6aad6dd8df..247b7e2b04 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToEnumConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpStringToEnumConverter.cs @@ -10,6 +10,10 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters { private readonly JsonStringEnumConverter _innerJsonStringEnumConverter; + private JsonSerializerOptions _readJsonSerializerOptions; + + private JsonSerializerOptions _writeJsonSerializerOptions; + public AbpStringToEnumConverter() : this(namingPolicy: null, allowIntegerValues: true) { @@ -28,21 +32,21 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, x => - x == this || - x.GetType() == typeof(AbpStringToEnumFactory)); + _readJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, x => + x == this || + x.GetType() == typeof(AbpStringToEnumFactory), + _innerJsonStringEnumConverter.CreateConverter(typeToConvert, options)); - newOptions.Converters.Add(_innerJsonStringEnumConverter.CreateConverter(typeToConvert, newOptions)); - return JsonSerializer.Deserialize(ref reader, newOptions); + return JsonSerializer.Deserialize(ref reader, _readJsonSerializerOptions); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, x => + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, x => x == this || x.GetType() == typeof(AbpStringToEnumFactory)); - JsonSerializer.Serialize(writer, value, newOptions); + JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions); } } } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs index 9ccc6738a6..6694c589cb 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs @@ -24,6 +24,14 @@ namespace Volo.Abp.Localization.Json } } + private static readonly JsonSerializerOptions DeserializeOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true + }; + /// /// Builds an from given json string. /// @@ -33,15 +41,7 @@ namespace Volo.Abp.Localization.Json JsonLocalizationFile jsonFile; try { - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Skip, - AllowTrailingCommas = true - }; - - jsonFile = JsonSerializer.Deserialize(jsonString, options); + jsonFile = JsonSerializer.Deserialize(jsonString, DeserializeOptions); } catch (JsonException ex) { diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs index d068015581..1e87049ed7 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Volo.Abp.Data; using Volo.Abp.DynamicProxy; @@ -9,6 +10,7 @@ namespace Volo.Abp.ObjectExtending [Serializable] public class ExtensibleObject : IHasExtraProperties, IValidatableObject { + [JsonInclude] public ExtraPropertyDictionary ExtraProperties { get; protected set; } public ExtensibleObject() diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToBoolean_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToBoolean_Tests.cs new file mode 100644 index 0000000000..c46a616db0 --- /dev/null +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToBoolean_Tests.cs @@ -0,0 +1,54 @@ +using System.Text.Json; +using Shouldly; +using Volo.Abp.Json.SystemTextJson.JsonConverters; +using Xunit; + +namespace Volo.Abp.Json +{ + public class AbpStringToBoolean_Tests + { + [Fact] + public void Test_Read() + { + var options = new JsonSerializerOptions() + { + Converters = + { + new AbpStringToBooleanConverter() + } + }; + + var testClass = JsonSerializer.Deserialize("{\"Enabled\": \"TrUe\"}", options); + testClass.ShouldNotBeNull(); + testClass.Enabled.ShouldBe(true); + + testClass = JsonSerializer.Deserialize("{\"Enabled\": true}", options); + testClass.ShouldNotBeNull(); + testClass.Enabled.ShouldBe(true); + } + + [Fact] + public void Test_Write() + { + var options = new JsonSerializerOptions() + { + Converters = + { + new AbpStringToBooleanConverter() + } + }; + + var testClassJson = JsonSerializer.Serialize(new TestClass() + { + Enabled = true + }); + + testClassJson.ShouldBe("{\"Enabled\":true}"); + } + + class TestClass + { + public bool Enabled { get; set; } + } + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToEnum_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToEnum_Tests.cs new file mode 100644 index 0000000000..d3bba36534 --- /dev/null +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpStringToEnum_Tests.cs @@ -0,0 +1,55 @@ +using System; +using System.Text.Json; +using Shouldly; +using Volo.Abp.Json.SystemTextJson.JsonConverters; +using Xunit; + +namespace Volo.Abp.Json +{ + public class AbpStringToEnum_Tests + { + [Fact] + public void Test_Read() + { + var options = new JsonSerializerOptions() + { + Converters = + { + new AbpStringToEnumFactory() + } + }; + + var testClass = JsonSerializer.Deserialize("{\"Day\": \"Monday\"}", options); + testClass.ShouldNotBeNull(); + testClass.Day.ShouldBe(DayOfWeek.Monday); + + testClass = JsonSerializer.Deserialize("{\"Day\": 1}", options); + testClass.ShouldNotBeNull(); + testClass.Day.ShouldBe(DayOfWeek.Monday); + } + + [Fact] + public void Test_Write() + { + var options = new JsonSerializerOptions() + { + Converters = + { + new AbpStringToEnumFactory() + } + }; + + var testClassJson = JsonSerializer.Serialize(new TestClass() + { + Day = DayOfWeek.Monday + }); + + testClassJson.ShouldBe("{\"Day\":1}"); + } + + class TestClass + { + public DayOfWeek Day { get; set; } + } + } +} diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index 3f7ce5dc26..3ceca6a2c2 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -174,8 +174,7 @@ namespace Volo.Abp.Json { var json = "{\"name\":\"test\",\"extraProperties\":null}"; var extensibleObject = JsonSerializer.Deserialize(json); - extensibleObject.ExtraProperties.ShouldNotBeNull(); - extensibleObject.ExtraProperties.ShouldBeEmpty(); + extensibleObject.ExtraProperties.ShouldBeNull(); } [Fact] diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHasExtraPropertiesJsonConverter_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/ExtensibleObject_Tests.cs similarity index 62% rename from framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHasExtraPropertiesJsonConverter_Tests.cs rename to framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/ExtensibleObject_Tests.cs index c11e8f3cec..9a88705282 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHasExtraPropertiesJsonConverter_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/ExtensibleObject_Tests.cs @@ -7,11 +7,11 @@ using Xunit; namespace Volo.Abp.Json { - public class AbpHasExtraPropertiesJsonConverter_Tests: AbpJsonTestBase + public class ExtensibleObject_Tests: AbpJsonTestBase { private readonly IJsonSerializer _jsonSerializer; - public AbpHasExtraPropertiesJsonConverter_Tests() + public ExtensibleObject_Tests() { _jsonSerializer = GetRequiredService(); } @@ -60,17 +60,54 @@ namespace Volo.Abp.Json fooDto.BarDtos.First().Name.ShouldBe("new-bar-dto"); fooDto.BarDtos.First().GetProperty("bar").ShouldBe("new-bar-value"); } + + [Fact] + public void SelfReference_Test() + { + var parentNodeDto = new NodeDto + { + Name = "parentNode", + }; + parentNodeDto.SetProperty("node", "parent-value"); + + var nodeDto = new NodeDto + { + Name = "node", + Parent = parentNodeDto + }; + nodeDto.SetProperty("node", "node-value"); + + var json = _jsonSerializer.Serialize(nodeDto); + + nodeDto = _jsonSerializer.Deserialize(json); + nodeDto.ShouldNotBeNull(); + nodeDto.Name.ShouldBe("node"); + nodeDto.GetProperty("node").ShouldBe("node-value"); + + nodeDto.Parent.ShouldNotBeNull(); + nodeDto.Parent.Name.ShouldBe("parentNode"); + nodeDto.Parent.GetProperty("node").ShouldBe("parent-value"); + } } - public class FooDto : ExtensibleObject + class FooDto : ExtensibleObject { public string Name { get; set; } public List BarDtos { get; set; } } - public class BarDto : ExtensibleObject + class BarDto : ExtensibleObject { public string Name { get; set; } + + public FooDto FooDto { get; set; } + } + + class NodeDto : ExtensibleObject + { + public string Name { get; set; } + + public NodeDto Parent { get; set; } } } diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs index 2a26c6ee42..4d1fb34b73 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs @@ -8,6 +8,8 @@ namespace Volo.Abp.MemoryDb.JsonConverters public class EntityJsonConverter : JsonConverter where TEntity : Entity { + private JsonSerializerOptions _writeJsonSerializerOptions; + public override TEntity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var jsonDocument = JsonDocument.ParseValue(ref reader); @@ -32,8 +34,8 @@ namespace Volo.Abp.MemoryDb.JsonConverters public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - JsonSerializer.Serialize(writer, value, newOptions); + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); + JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs index 9d62f9952c..7229ecdd83 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs @@ -8,6 +8,10 @@ namespace Volo.Abp.FeatureManagement.JsonConverters { public class SelectionStringValueItemSourceJsonConverter : JsonConverter { + private JsonSerializerOptions _readJsonSerializerOptions; + + private JsonSerializerOptions _writeJsonSerializerOptions; + public override ISelectionStringValueItemSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; @@ -16,10 +20,10 @@ namespace Volo.Abp.FeatureManagement.JsonConverters if (itemsJsonProperty.Value.ValueKind == JsonValueKind.Array) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); + _readJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); var selectionStringValueItem = - itemsJsonProperty.Value.Deserialize(newOptions) ?? + itemsJsonProperty.Value.Deserialize(_readJsonSerializerOptions) ?? Array.Empty(); return new StaticSelectionStringValueItemSource(selectionStringValueItem.As()); @@ -30,8 +34,8 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - JsonSerializer.Serialize(writer, value, value.GetType(), newOptions); + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); + JsonSerializer.Serialize(writer, value, value.GetType(), _writeJsonSerializerOptions); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs index 408ef2f99b..992ba4a183 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs @@ -8,6 +8,10 @@ namespace Volo.Abp.FeatureManagement.JsonConverters { public class StringValueTypeJsonConverter : JsonConverter { + private JsonSerializerOptions _readJsonSerializerOptions; + + private JsonSerializerOptions _writeJsonSerializerOptions; + public override IStringValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; @@ -17,14 +21,13 @@ namespace Volo.Abp.FeatureManagement.JsonConverters { var name = nameJsonProperty.Value.GetString(); - var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), - new SelectionStringValueItemSourceJsonConverter()); + _readJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), new SelectionStringValueItemSourceJsonConverter()); return name switch { - "SelectionStringValueType" => rootElement.Deserialize(newOptions), - "FreeTextStringValueType" => rootElement.Deserialize(newOptions), - "ToggleStringValueType" => rootElement.Deserialize(newOptions), + "SelectionStringValueType" => rootElement.Deserialize(_readJsonSerializerOptions), + "FreeTextStringValueType" => rootElement.Deserialize(_readJsonSerializerOptions), + "ToggleStringValueType" => rootElement.Deserialize(_readJsonSerializerOptions), _ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!") }; } @@ -34,8 +37,8 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override void Write(Utf8JsonWriter writer, IStringValueType value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - JsonSerializer.Serialize(writer, value, value.GetType(), newOptions); + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); + JsonSerializer.Serialize(writer, value, value.GetType(), _writeJsonSerializerOptions); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs index 27f92cc0ec..5cd51d09c4 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs @@ -10,6 +10,10 @@ namespace Volo.Abp.FeatureManagement.JsonConverters { public class ValueValidatorJsonConverter : JsonConverter { + private JsonSerializerOptions _readJsonSerializerOptions; + + private JsonSerializerOptions _writeJsonSerializerOptions; + public override IValueValidator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; @@ -22,8 +26,8 @@ namespace Volo.Abp.FeatureManagement.JsonConverters var propertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase)); if (propertiesJsonProperty.Value.ValueKind == JsonValueKind.Object) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); - var properties = propertiesJsonProperty.Value.Deserialize>(newOptions); + _readJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); + var properties = propertiesJsonProperty.Value.Deserialize>(_readJsonSerializerOptions); if (properties != null && properties.Any()) { foreach (var property in properties) @@ -41,8 +45,8 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override void Write(Utf8JsonWriter writer, IValueValidator value, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - JsonSerializer.Serialize(writer, value, value.GetType(), newOptions); + _writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(options, this); + JsonSerializer.Serialize(writer, value, value.GetType(), _writeJsonSerializerOptions); } protected virtual IValueValidator CreateValueValidatorByName(string name)