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 366906f66e..bc5f4bef28 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 @@ -27,6 +27,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Json options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); + + options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); + options.JsonSerializerOptions.Converters.Add(new AbpExtraPropertyDictionaryJsonConverterFactory()); } } } diff --git a/framework/src/Volo.Abp.Core/System/Text/Json/JsonSerializerOptionsHelper.cs b/framework/src/Volo.Abp.Core/System/Text/Json/JsonSerializerOptionsHelper.cs new file mode 100644 index 0000000000..eea80f2fb5 --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Text/Json/JsonSerializerOptionsHelper.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace System.Text.Json +{ + public static class JsonSerializerOptionsHelper + { + public static JsonSerializerOptions Create(JsonSerializerOptions baseOptions, JsonConverter removeConverter, params JsonConverter[] addConverters) + { + return Create(baseOptions, x => x == removeConverter, addConverters); + } + + public static JsonSerializerOptions Create(JsonSerializerOptions baseOptions, Func removeConverterPredicate, params JsonConverter[] addConverters) + { + var options = new JsonSerializerOptions(baseOptions); + options.Converters.RemoveAll(removeConverterPredicate); + options.Converters.AddIfNotContains(addConverters); + return options; + } + } +} diff --git a/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj b/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj index 1ef71853fa..ef88ab752b 100644 --- a/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj +++ b/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj @@ -16,6 +16,7 @@ + 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 626e084ca6..a5d6d111b1 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 @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.Json.SystemTextJson.JsonConverters; @@ -21,6 +21,9 @@ namespace Volo.Abp.Json.SystemTextJson options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); + + options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); + options.JsonSerializerOptions.Converters.Add(new AbpExtraPropertyDictionaryJsonConverterFactory()); } } } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverter.cs new file mode 100644 index 0000000000..f816cb6396 --- /dev/null +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Volo.Abp.Data; +using Volo.Abp.ObjectExtending; + +namespace Volo.Abp.Json.SystemTextJson.JsonConverters +{ + public class AbpExtraPropertyDictionaryJsonConverter : JsonConverter + where T : ExtensibleObject + { + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var newOptions = JsonSerializerOptionsHelper.Create(options, x => + x == this || + x.GetType() == typeof(AbpExtraPropertyDictionaryJsonConverterFactory)); + + var rootElement = JsonDocument.ParseValue(ref reader).RootElement; + var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); + var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => + x.Name.Equals(nameof(ExtensibleObject.ExtraProperties), StringComparison.OrdinalIgnoreCase)) + .Value.GetRawText(); + + var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties, typeof(ExtraPropertyDictionary), newOptions); + + ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); + + return extensibleObject; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + var newOptions = JsonSerializerOptionsHelper.Create(options, x => + x == this || + x.GetType() == typeof(AbpExtraPropertyDictionaryJsonConverterFactory)); + + JsonSerializer.Serialize(writer, value, newOptions); + } + } +} diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverterFactory.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverterFactory.cs new file mode 100644 index 0000000000..9dabd86b85 --- /dev/null +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverterFactory.cs @@ -0,0 +1,26 @@ +using System; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using Volo.Abp.ObjectExtending; + +namespace Volo.Abp.Json.SystemTextJson.JsonConverters +{ + public class AbpExtraPropertyDictionaryJsonConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(ExtensibleObject) || typeToConvert.IsSubclassOf(typeof(ExtensibleObject)); + } + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter) Activator.CreateInstance( + typeof(AbpExtraPropertyDictionaryJsonConverter<>).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 dcfe08f5c3..cfd5c17653 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 @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.Buffers.Text; using System.Text.Json; @@ -29,8 +29,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options); - newOptions.Converters.Remove(this); + var newOptions = JsonSerializerOptionsHelper.Create(options, this); var entityConverter = (JsonConverter)newOptions.GetConverter(typeof(bool)); entityConverter.Write(writer, value, newOptions); } 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 cead695538..6aad6dd8df 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 @@ -28,16 +28,20 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options); - newOptions.Converters.RemoveAll(x => x == this || x.GetType() == typeof(AbpStringToEnumFactory)); + var newOptions = JsonSerializerOptionsHelper.Create(options, x => + x == this || + x.GetType() == typeof(AbpStringToEnumFactory)); + newOptions.Converters.Add(_innerJsonStringEnumConverter.CreateConverter(typeToConvert, newOptions)); return JsonSerializer.Deserialize(ref reader, newOptions); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options); - newOptions.Converters.RemoveAll(x => x == this || x.GetType() == typeof(AbpStringToEnumFactory)); + var newOptions = JsonSerializerOptionsHelper.Create(options, x => + x == this || + x.GetType() == typeof(AbpStringToEnumFactory)); + JsonSerializer.Serialize(writer, value, newOptions); } } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs index 02fa9bbaff..cdf2257a32 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs @@ -51,7 +51,8 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override void Write(Utf8JsonWriter writer, object objectToWrite, JsonSerializerOptions options) { - throw new InvalidOperationException("Should not get here."); + var newOptions = JsonSerializerOptionsHelper.Create(options, this); + JsonSerializer.Serialize(writer, objectToWrite, newOptions); } } } 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 dd4a366b03..da49f7ebc6 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 @@ -1,5 +1,7 @@ using Shouldly; +using Volo.Abp.Data; using Volo.Abp.Json.SystemTextJson; +using Volo.Abp.ObjectExtending; using Xunit; namespace Volo.Abp.Json @@ -78,6 +80,24 @@ namespace Volo.Abp.Json newJson.ShouldBe("{\"name\":\"abp\",\"type\":2}"); } + + [Fact] + public void Serialize_Deserialize_ExtensibleObject() + { + var json = "{\"name\":\"test\",\"extraProperties\":{\"One\":\"123\",\"Two\":456}}"; + var extensibleObject = _jsonSerializer.Deserialize(json); + extensibleObject.GetProperty("One").ShouldBe("123"); + extensibleObject.GetProperty("Two").ShouldBe(456); + + var newJson = _jsonSerializer.Serialize(extensibleObject); + newJson.ShouldBe(json); + } + + class TestExtensibleObjectClass : ExtensibleObject + { + public string Name { get; set; } + } + class FileWithBoolean { public string Name { 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 576e7512fa..fbc222a46a 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 @@ -25,8 +25,7 @@ namespace Volo.Abp.MemoryDb.JsonConverters public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options); - newOptions.Converters.Remove(this); + var newOptions = JsonSerializerOptionsHelper.Create(options, this); var entityConverter = (JsonConverter)newOptions.GetConverter(typeof(TEntity)); entityConverter.Write(writer, value, newOptions); } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/JsonSerializerOptionsHelper.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/JsonSerializerOptionsHelper.cs deleted file mode 100644 index 26927f59fe..0000000000 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/JsonSerializerOptionsHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Volo.Abp.FeatureManagement.JsonConverters -{ - internal static class JsonSerializerOptionsHelper - { - public static JsonSerializerOptions Create(JsonSerializerOptions baseOptions, JsonConverter removeConverter, params JsonConverter[] addConverters) - { - var options = new JsonSerializerOptions(baseOptions); - options.Converters.RemoveAll(x => x == removeConverter); - options.Converters.AddIfNotContains(addConverters); - return options; - } - } -} 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 feaa68ccc6..3a05e3f63f 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 @@ -14,13 +14,13 @@ namespace Volo.Abp.FeatureManagement.JsonConverters var newOptions = JsonSerializerOptionsHelper.Create(options, this); var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var items = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals("Items", StringComparison.InvariantCultureIgnoreCase)).Value.GetRawText(); + var items = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(ISelectionStringValueItemSource.Items), StringComparison.OrdinalIgnoreCase)).Value.GetRawText(); var selectionStringValueItem = JsonSerializer.Deserialize>(items, newOptions) ?? new List(); - return new StaticSelectionStringValueItemSource(selectionStringValueItem.ToArray()); + return new StaticSelectionStringValueItemSource(selectionStringValueItem.ToArray().As()); } public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options) 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 32748231da..08ba6326b9 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 @@ -11,7 +11,7 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override IStringValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals("Name", StringComparison.InvariantCultureIgnoreCase)).Value.GetString(); + var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IStringValueType.Name), StringComparison.OrdinalIgnoreCase)).Value.GetString(); var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), new SelectionStringValueItemSourceJsonConverter()); return name switch { 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 7276a36923..98c1013c12 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 @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; @@ -13,10 +13,10 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override IValueValidator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals("Name", StringComparison.InvariantCultureIgnoreCase)).Value.GetString(); + var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Name), StringComparison.OrdinalIgnoreCase)).Value.GetString(); var valueValidator = CreateValueValidatorByName(name); - var propertiesRawText = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals("Properties", StringComparison.InvariantCultureIgnoreCase)).Value.GetRawText(); + var propertiesRawText = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase)).Value.GetRawText(); var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); var properties = JsonSerializer.Deserialize>(propertiesRawText, newOptions); if (properties != null && properties.Any())