diff --git a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
index 15b967b9e4..5909bb632f 100644
--- a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
+++ b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
@@ -25,6 +25,7 @@
+
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
index 47640ec306..ebd561c0ae 100644
--- 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
@@ -11,9 +11,18 @@ 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(AbpHasExtraPropertiesJsonConverterFactory));
+ 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)
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
index f654504e05..dff25e00df 100644
--- 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
@@ -1,5 +1,7 @@
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;
@@ -11,8 +13,26 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
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))
{
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/AbpHasExtraPropertiesJsonConverter_Tests.cs
new file mode 100644
index 0000000000..c11e8f3cec
--- /dev/null
+++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHasExtraPropertiesJsonConverter_Tests.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using System.Linq;
+using Shouldly;
+using Volo.Abp.Data;
+using Volo.Abp.ObjectExtending;
+using Xunit;
+
+namespace Volo.Abp.Json
+{
+ public class AbpHasExtraPropertiesJsonConverter_Tests: AbpJsonTestBase
+ {
+ private readonly IJsonSerializer _jsonSerializer;
+
+ public AbpHasExtraPropertiesJsonConverter_Tests()
+ {
+ _jsonSerializer = GetRequiredService();
+ }
+
+ [Fact]
+ public void JsonConverter_Test()
+ {
+ var fooDto = new FooDto
+ {
+ Name = "foo-dto",
+ BarDtos = new List()
+ };
+ fooDto.SetProperty("foo", "foo-value");
+
+ var barDto = new BarDto
+ {
+ Name = "bar-dto"
+ };
+ barDto.SetProperty("bar", "bar-value");
+ fooDto.BarDtos.Add(barDto);
+
+ var json = _jsonSerializer.Serialize(fooDto);
+
+ fooDto = _jsonSerializer.Deserialize(json);
+ fooDto.ShouldNotBeNull();
+ fooDto.Name.ShouldBe("foo-dto");
+ fooDto.GetProperty("foo").ShouldBe("foo-value");
+
+ fooDto.BarDtos.Count.ShouldBe(1);
+ fooDto.BarDtos.First().Name.ShouldBe("bar-dto");
+ fooDto.BarDtos.First().GetProperty("bar").ShouldBe("bar-value");
+
+ fooDto.Name = "new-foo-dto";
+ fooDto.SetProperty("foo", "new-foo-value");
+ fooDto.BarDtos.First().Name = "new-bar-dto";
+ fooDto.BarDtos.First().SetProperty("bar", "new-bar-value");
+
+ json = _jsonSerializer.Serialize(fooDto);
+
+ fooDto = _jsonSerializer.Deserialize(json);
+ fooDto.ShouldNotBeNull();
+ fooDto.Name.ShouldBe("new-foo-dto");
+ fooDto.GetProperty("foo").ShouldBe("new-foo-value");
+
+ fooDto.BarDtos.Count.ShouldBe(1);
+ fooDto.BarDtos.First().Name.ShouldBe("new-bar-dto");
+ fooDto.BarDtos.First().GetProperty("bar").ShouldBe("new-bar-value");
+ }
+ }
+
+ public class FooDto : ExtensibleObject
+ {
+ public string Name { get; set; }
+
+ public List BarDtos { get; set; }
+ }
+
+ public class BarDto : ExtensibleObject
+ {
+ public string Name { get; set; }
+ }
+}