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.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..5a4ef9d2df --- /dev/null +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpExtraPropertyDictionaryJsonConverter.cs @@ -0,0 +1,39 @@ +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 = new JsonSerializerOptions(options); + newOptions.Converters.RemoveAll(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 = new JsonSerializerOptions(options); + newOptions.Converters.RemoveAll(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/ObjectToInferredTypesConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs index 02fa9bbaff..c323293dd9 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,9 @@ 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 = new JsonSerializerOptions(options); + newOptions.Converters.Remove(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; }