Browse Source

Merge pull request #5952 from abpframework/maliming/system.text.json

Move to System.Text.Json
pull/6054/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
9680a443f0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      framework/Volo.Abp.sln
  2. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  3. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  4. 39
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs
  5. 37
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs
  6. 28
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs
  7. 19
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs
  8. 22
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcNewtonsoftJsonOptionsSetup.cs
  9. 22
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MediaTypeHeaderValues.cs
  10. 83
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs
  11. 4
      framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
  12. 20
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingContractResolver.cs
  13. 2
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/JsonNetAuditSerializer.cs
  14. 39
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
  15. 12
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs
  16. 15
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs
  17. 15
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs
  18. 4
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs
  19. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs
  20. 4
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs
  21. 6
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerInterfaceApiDescriptionModel.cs
  22. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs
  23. 14
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs
  24. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs
  25. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs
  26. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs
  27. 5
      framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj
  28. 62
      framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpHybridJsonSerializer.cs
  29. 15
      framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonModule.cs
  30. 13
      framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonOptions.cs
  31. 15
      framework/src/Volo.Abp.Json/Volo/Abp/Json/IJsonSerializerProvider.cs
  32. 9
      framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerProvider.cs
  33. 23
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptions.cs
  34. 24
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs
  35. 59
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs
  36. 21
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonUnsupportedTypeMatcher.cs
  37. 38
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs
  38. 50
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs
  39. 57
      framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs
  40. 4
      framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj
  41. 21
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs
  42. 4
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationFile.cs
  43. 41
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializer.cs
  44. 14
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializerOptions.cs
  45. 1
      framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction_Tests.cs
  46. 10
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController.cs
  47. 43
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs
  48. 14
      framework/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpAspNetCoreTestBase.cs
  49. 67
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/JsonNetAuditSerializer_Test.cs
  50. 16
      framework/test/Volo.Abp.Json.Tests/Volo.Abp.Json.Tests.csproj
  51. 125
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHybridJsonSerializer_Tests.cs
  52. 12
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs
  53. 15
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestModule.cs
  54. 66
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonUnsupportedTypeMatcher_Tests.cs
  55. 12
      framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/AbpMemoryDbTestModule.cs
  56. 33
      framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs
  57. 1
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs
  58. 4
      modules/docs/src/Volo.Docs.Domain.Shared/Volo.Docs.Domain.Shared.csproj
  59. 14
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs
  60. 17
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/JsonSerializerOptionsHelper.cs
  61. 32
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs
  62. 31
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs
  63. 51
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs
  64. 90
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/StringValueTypeJsonConverter.cs
  65. 10
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs
  66. 4
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs
  67. 9
      templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs

7
framework/Volo.Abp.sln

@ -355,6 +355,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Compone
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Swashbuckle", "src\Volo.Abp.Swashbuckle\Volo.Abp.Swashbuckle.csproj", "{DD9519E0-5A68-48DC-A051-7BF2AC922F3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Tests", "test\Volo.Abp.Json.Tests\Volo.Abp.Json.Tests.csproj", "{00D07595-993C-40FC-BD90-0DD6331414D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1057,6 +1059,10 @@ Global
{DD9519E0-5A68-48DC-A051-7BF2AC922F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD9519E0-5A68-48DC-A051-7BF2AC922F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD9519E0-5A68-48DC-A051-7BF2AC922F3E}.Release|Any CPU.Build.0 = Release|Any CPU
{00D07595-993C-40FC-BD90-0DD6331414D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00D07595-993C-40FC-BD90-0DD6331414D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00D07595-993C-40FC-BD90-0DD6331414D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00D07595-993C-40FC-BD90-0DD6331414D3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1236,6 +1242,7 @@ Global
{B9D1ADCB-D552-4626-A1F1-78FF72C1E822} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{89840441-5A3A-4FD7-9CB4-E5B52FAEF72A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DD9519E0-5A68-48DC-A051-7BF2AC922F3E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{00D07595-993C-40FC-BD90-0DD6331414D3} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -28,8 +28,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.0-rc.2.*" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0-rc.2.*" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.1.1" />
</ItemGroup>
</Project>

8
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -35,6 +35,7 @@ using Volo.Abp.Http;
using Volo.Abp.DynamicProxy;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.UI;
@ -115,11 +116,6 @@ namespace Volo.Abp.AspNetCore.Mvc
);
var mvcBuilder = context.Services.AddMvc()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver =
new AbpMvcJsonContractResolver(context.Services);
})
.AddRazorRuntimeCompilation()
.AddDataAnnotationsLocalization(options =>
{
@ -140,6 +136,8 @@ namespace Volo.Abp.AspNetCore.Mvc
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
mvcCoreBuilder.AddAbpHybridJson();
Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
options.FileProviders.Add(

39
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonInputFormatter.cs

@ -0,0 +1,39 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json.SystemTextJson;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public class AbpHybridJsonInputFormatter : TextInputFormatter, IInputFormatterExceptionPolicy
{
public AbpHybridJsonInputFormatter()
{
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson);
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
}
public async override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
return await GetTextInputFormatter(context).ReadRequestBodyAsync(context, encoding);
}
protected virtual TextInputFormatter GetTextInputFormatter(InputFormatterContext context)
{
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonUnsupportedTypeMatcher>();
if (!typesMatcher.Match(context.ModelType))
{
return context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonInputFormatter>();
}
return context.HttpContext.RequestServices.GetRequiredService<NewtonsoftJsonInputFormatter>();
}
public virtual InputFormatterExceptionPolicy ExceptionPolicy => InputFormatterExceptionPolicy.MalformedInputExceptions;
}
}

37
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpHybridJsonOutputFormatter.cs

@ -0,0 +1,37 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json.SystemTextJson;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public class AbpHybridJsonOutputFormatter : TextOutputFormatter
{
public AbpHybridJsonOutputFormatter()
{
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson);
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
}
public async override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
await GetTextInputFormatter(context).WriteResponseBodyAsync(context, selectedEncoding);
}
protected virtual TextOutputFormatter GetTextInputFormatter(OutputFormatterWriteContext context)
{
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonUnsupportedTypeMatcher>();
if (!typesMatcher.Match(context.ObjectType))
{
return context.HttpContext.RequestServices.GetRequiredService<SystemTextJsonOutputFormatter>();
}
return context.HttpContext.RequestServices.GetRequiredService<NewtonsoftJsonOutputFormatter>();
}
}
}

28
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs

@ -0,0 +1,28 @@
using System;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public class AbpJsonOptionsSetup : IConfigureOptions<JsonOptions>
{
protected IServiceProvider ServiceProvider { get; }
public AbpJsonOptionsSetup(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Configure(JsonOptions options)
{
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
options.JsonSerializerOptions.AllowTrailingCommas = true;
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpDateTimeConverter>());
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpNullableDateTimeConverter>());
}
}
}

19
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs

@ -1,28 +1,32 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Reflection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Reflection;
using Volo.Abp.Timing;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public class AbpMvcJsonContractResolver : DefaultContractResolver
public class AbpMvcJsonContractResolver : DefaultContractResolver, ITransientDependency
{
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter;
public AbpMvcJsonContractResolver(IServiceCollection services)
public AbpMvcJsonContractResolver(IServiceProvider serviceProvider)
{
_dateTimeConverter = services.GetServiceLazy<AbpJsonIsoDateTimeConverter>();
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>(
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>,
true
);
NamingStrategy = new CamelCaseNamingStrategy();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var property = base.CreateProperty(member, memberSerialization);
ModifyProperty(member, property);
@ -40,7 +44,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
{
property.Converter = _dateTimeConverter.Value;
}
}
}
}

22
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcNewtonsoftJsonOptionsSetup.cs

@ -0,0 +1,22 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public class AbpMvcNewtonsoftJsonOptionsSetup : IConfigureOptions<MvcNewtonsoftJsonOptions>
{
protected IServiceProvider ServiceProvider { get; }
public AbpMvcNewtonsoftJsonOptionsSetup(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Configure(MvcNewtonsoftJsonOptions options)
{
options.SerializerSettings.ContractResolver = ServiceProvider.GetRequiredService<AbpMvcJsonContractResolver>();
}
}
}

22
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MediaTypeHeaderValues.cs

@ -0,0 +1,22 @@
using Microsoft.Net.Http.Headers;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
/// <summary>
/// https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.NewtonsoftJson/src/MediaTypeHeaderValues.cs
/// </summary>
internal static class MediaTypeHeaderValues
{
public static readonly MediaTypeHeaderValue ApplicationJson = MediaTypeHeaderValue.Parse("application/json").CopyAsReadOnly();
public static readonly MediaTypeHeaderValue TextJson = MediaTypeHeaderValue.Parse("text/json").CopyAsReadOnly();
public static readonly MediaTypeHeaderValue ApplicationAnyJsonSyntax = MediaTypeHeaderValue.Parse("application/*+json").CopyAsReadOnly();
public static readonly MediaTypeHeaderValue ApplicationXml = MediaTypeHeaderValue.Parse("application/xml").CopyAsReadOnly();
public static readonly MediaTypeHeaderValue TextXml = MediaTypeHeaderValue.Parse("text/xml").CopyAsReadOnly();
public static readonly MediaTypeHeaderValue ApplicationAnyXmlSyntax = MediaTypeHeaderValue.Parse("application/*+xml").CopyAsReadOnly();
}
}

83
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/MvcCoreBuilderExtensions.cs

@ -0,0 +1,83 @@
using System.Buffers;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
namespace Volo.Abp.AspNetCore.Mvc.Json
{
public static class MvcCoreBuilderExtensions
{
public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder)
{
//SystemTextJsonInputFormatter
builder.Services.AddTransient(provider =>
{
var jsonOptions = provider.GetRequiredService<IOptions<JsonOptions>>();
var logger = provider.GetRequiredService<ILoggerFactory>().CreateLogger<SystemTextJsonInputFormatter>();
return new SystemTextJsonInputFormatter(jsonOptions.Value, logger);
});
builder.Services.AddTransient<DefaultObjectPoolProvider>();
//NewtonsoftJsonInputFormatter
builder.Services.AddTransient(provider =>
{
var jsonOptions = provider.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value;
return new NewtonsoftJsonInputFormatter(
provider.GetRequiredService<ILoggerFactory>().CreateLogger<NewtonsoftJsonInputFormatter>(),
jsonOptions.SerializerSettings,
provider.GetRequiredService<ArrayPool<char>>(),
provider.GetRequiredService<DefaultObjectPoolProvider>(),
provider.GetRequiredService<IOptions<MvcOptions>>().Value,
jsonOptions);
});
//SystemTextJsonOutputFormatter
builder.Services.AddTransient(provider =>
{
var jsonSerializerOptions = provider.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions;
if (jsonSerializerOptions.Encoder is null)
{
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
jsonSerializerOptions = new JsonSerializerOptions(jsonSerializerOptions)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
}
return new SystemTextJsonOutputFormatter(jsonSerializerOptions);
});
//NewtonsoftJsonOutputFormatter
builder.Services.AddTransient(provider =>
{
var jsonOptions = provider.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value;
return new NewtonsoftJsonOutputFormatter(
jsonOptions.SerializerSettings,
provider.GetRequiredService<ArrayPool<char>>(),
provider.GetRequiredService<IOptions<MvcOptions>>().Value);
});
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<JsonOptions>, AbpJsonOptionsSetup>());
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>());
builder.Services.Configure<MvcOptions>(options =>
{
options.InputFormatters.RemoveType<SystemTextJsonInputFormatter>();
options.InputFormatters.RemoveType<NewtonsoftJsonInputFormatter>();
options.InputFormatters.Add(new AbpHybridJsonInputFormatter());
options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
options.OutputFormatters.RemoveType<NewtonsoftJsonOutputFormatter>();
options.OutputFormatters.Add(new AbpHybridJsonOutputFormatter());
});
return builder;
}
}
}

4
framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj

@ -23,4 +23,8 @@
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

20
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingContractResolver.cs

@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@ -19,18 +20,21 @@ namespace Volo.Abp.Auditing
{
var property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(DisableAuditingAttribute)) || member.IsDefined(typeof(JsonIgnoreAttribute)))
if (_ignoredTypes.Any(ignoredType => ignoredType.GetTypeInfo().IsAssignableFrom(property.PropertyType)))
{
property.ShouldSerialize = instance => false;
return property;
}
foreach (var ignoredType in _ignoredTypes)
if (member.DeclaringType != null && (member.DeclaringType.IsDefined(typeof(DisableAuditingAttribute)) || member.DeclaringType.IsDefined(typeof(JsonIgnoreAttribute))))
{
if (ignoredType.GetTypeInfo().IsAssignableFrom(property.PropertyType))
{
property.ShouldSerialize = instance => false;
break;
}
property.ShouldSerialize = instance => false;
return property;
}
if (member.IsDefined(typeof(DisableAuditingAttribute)) || member.IsDefined(typeof(JsonIgnoreAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;

2
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/JsonNetAuditSerializer.cs

@ -41,4 +41,4 @@ namespace Volo.Abp.Auditing
return _sharedJsonSerializerSettings;
}
}
}
}

39
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs

@ -77,7 +77,7 @@ namespace Volo.Abp.Domain.Entities
{
var entity1Key = entity1Keys[i];
var entity2Key = entity2Keys[i];
if (entity1Key == null)
{
if (entity2Key == null)
@ -89,13 +89,13 @@ namespace Volo.Abp.Domain.Entities
//entity2Key is not null!
return false;
}
if (entity2Key == null)
{
//entity1Key was not null!
return false;
}
if (TypeHelper.IsDefaultValue(entity1Key) && TypeHelper.IsDefaultValue(entity2Key))
{
return false;
@ -266,5 +266,36 @@ namespace Volo.Abp.Domain.Entities
property?.SetValue(entity, idFactory());
}
public static void TrySetId<TKey>(
object entity,
TKey id,
bool checkForDisableIdGenerationAttribute = false)
{
var property = CachedIdProperties.GetOrAdd(
$"{entity.GetType().FullName}-{checkForDisableIdGenerationAttribute}", () =>
{
var idProperty = entity
.GetType()
.GetProperties()
.FirstOrDefault(x => x.Name == nameof(IEntity<object>.Id) &&
x.GetSetMethod(true) != null);
if (idProperty == null)
{
return null;
}
if (checkForDisableIdGenerationAttribute &&
idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true))
{
return null;
}
return idProperty;
});
property?.SetValue(entity, id);
}
}
}
}

12
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/AbpJsonValueConverter.cs

@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Newtonsoft.Json;
using Volo.Abp.Data;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
namespace Volo.Abp.EntityFrameworkCore.ValueConverters
{
@ -16,12 +16,14 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
private static string SerializeObject(TPropertyType d)
{
return JsonConvert.SerializeObject(d, Formatting.None);
return JsonSerializer.Serialize(d);
}
private static TPropertyType DeserializeObject(string s)
{
return JsonConvert.DeserializeObject<TPropertyType>(s);
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter());
return JsonSerializer.Deserialize<TPropertyType>(s, deserializeOptions);
}
}
}

15
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Newtonsoft.Json;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.EntityFrameworkCore.ValueConverters
@ -36,13 +36,14 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
}
}
return JsonConvert.SerializeObject(copyDictionary, Formatting.None);
return JsonSerializer.Serialize(copyDictionary);
}
private static Dictionary<string, object> DeserializeObject(string extraPropertiesAsJson, Type entityType)
{
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(extraPropertiesAsJson);
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ObjectToInferredTypesConverter());
var dictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(extraPropertiesAsJson, deserializeOptions) ?? new Dictionary<string, object>();
if (entityType != null)
{
var objectExtension = ObjectExtensionManager.Instance.GetOrNull(entityType);
@ -59,7 +60,7 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
}
private static object GetNormalizedValue(
Dictionary<string, object> dictionary,
Dictionary<string, object> dictionary,
ObjectExtensionPropertyInfo property)
{
var value = dictionary.GetOrDefault(property.Name);
@ -84,4 +85,4 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
}
}
}
}
}

15
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs

@ -2,9 +2,8 @@
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Threading;
@ -17,11 +16,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying
protected IApiDescriptionCache Cache { get; }
private static readonly JsonSerializerSettings SharedJsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
public ApiDescriptionFinder(IApiDescriptionCache cache)
{
Cache = cache;
@ -91,9 +85,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(
content,
typeof(ApplicationApiDescriptionModel), SharedJsonSerializerSettings);
var result = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(content, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
return (ApplicationApiDescriptionModel)result;
}

4
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs

@ -26,7 +26,7 @@ namespace Volo.Abp.Http.Modeling
public ReturnValueApiDescriptionModel ReturnValue { get; set; }
private ActionApiDescriptionModel()
public ActionApiDescriptionModel()
{
}
@ -71,4 +71,4 @@ namespace Volo.Abp.Http.Modeling
return $"[ActionApiDescriptionModel {Name}]";
}
}
}
}

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs

@ -12,9 +12,9 @@ namespace Volo.Abp.Http.Modeling
public IDictionary<string, TypeApiDescriptionModel> Types { get; set; }
private ApplicationApiDescriptionModel()
public ApplicationApiDescriptionModel()
{
}
public static ApplicationApiDescriptionModel Create()
@ -56,4 +56,4 @@ namespace Volo.Abp.Http.Modeling
return subModel;
}
}
}
}

4
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs

@ -16,7 +16,7 @@ namespace Volo.Abp.Http.Modeling
public Dictionary<string, ActionApiDescriptionModel> Actions { get; set; }
private ControllerApiDescriptionModel()
public ControllerApiDescriptionModel()
{
}
@ -79,4 +79,4 @@ namespace Volo.Abp.Http.Modeling
return $"[ControllerApiDescriptionModel {ControllerName}]";
}
}
}
}

6
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerInterfaceApiDescriptionModel.cs

@ -7,9 +7,9 @@ namespace Volo.Abp.Http.Modeling
{
public string Type { get; set; }
private ControllerInterfaceApiDescriptionModel()
public ControllerInterfaceApiDescriptionModel()
{
}
public static ControllerInterfaceApiDescriptionModel Create(Type type)
@ -20,4 +20,4 @@ namespace Volo.Abp.Http.Modeling
};
}
}
}
}

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs

@ -19,7 +19,7 @@ namespace Volo.Abp.Http.Modeling
public object DefaultValue { get; set; }
private MethodParameterApiDescriptionModel()
public MethodParameterApiDescriptionModel()
{
}

14
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs

@ -11,22 +11,22 @@ namespace Volo.Abp.Http.Modeling
/// <summary>
/// "app".
/// </summary>
public const string DefaultRootPath = "app";
public const string DefaultRootPath = "app";
/// <summary>
/// "Default".
/// </summary>
public const string DefaultRemoteServiceName = "Default";
public const string DefaultRemoteServiceName = "Default";
public string RootPath { get; set; }
public string RemoteServiceName { get; set; }
public IDictionary<string, ControllerApiDescriptionModel> Controllers { get; set; }
private ModuleApiDescriptionModel()
public ModuleApiDescriptionModel()
{
}
public static ModuleApiDescriptionModel Create(string rootPath, string remoteServiceName)
@ -53,7 +53,7 @@ namespace Volo.Abp.Http.Modeling
{
return Controllers.GetOrAdd(uniqueName, () => ControllerApiDescriptionModel.Create(name, type, ignoredInterfaces));
}
public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions)
{
var subModel = Create(RootPath, RemoteServiceName);
@ -69,4 +69,4 @@ namespace Volo.Abp.Http.Modeling
return subModel;
}
}
}
}

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs

@ -24,7 +24,7 @@ namespace Volo.Abp.Http.Modeling
public string DescriptorName { get; set; }
private ParameterApiDescriptionModel()
public ParameterApiDescriptionModel()
{
}

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs

@ -11,7 +11,7 @@ namespace Volo.Abp.Http.Modeling
public string TypeSimple { get; set; }
private ReturnValueApiDescriptionModel()
public ReturnValueApiDescriptionModel()
{
}

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs

@ -19,7 +19,7 @@ namespace Volo.Abp.Http.Modeling
public PropertyApiDescriptionModel[] Properties { get; set; }
private TypeApiDescriptionModel()
public TypeApiDescriptionModel()
{
}

5
framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj

@ -14,12 +14,9 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

62
framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpHybridJsonSerializer.cs

@ -0,0 +1,62 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json
{
public class AbpHybridJsonSerializer : IJsonSerializer, ITransientDependency
{
protected AbpJsonOptions Options { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public AbpHybridJsonSerializer(IOptions<AbpJsonOptions> options, IServiceScopeFactory serviceScopeFactory)
{
Options = options.Value;
ServiceScopeFactory = serviceScopeFactory;
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var serializerProvider = GetSerializerProvider(scope.ServiceProvider, obj.GetType());
return serializerProvider.Serialize(obj, camelCase, indented);
}
}
public T Deserialize<T>(string jsonString, bool camelCase = true)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var serializerProvider = GetSerializerProvider(scope.ServiceProvider, typeof(T));
return serializerProvider.Deserialize<T>(jsonString, camelCase);
}
}
public object Deserialize(Type type, string jsonString, bool camelCase = true)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var serializerProvider = GetSerializerProvider(scope.ServiceProvider, type);
return serializerProvider.Deserialize(type, jsonString, camelCase);
}
}
protected virtual IJsonSerializerProvider GetSerializerProvider(IServiceProvider serviceProvider, Type type)
{
foreach (var providerType in Options.Providers.Reverse())
{
var provider = serviceProvider.GetRequiredService(providerType) as IJsonSerializerProvider;
if (provider.CanHandle(type))
{
return provider;
}
}
throw new AbpException($"There is no IJsonSerializerProvider that can handle '{type.GetFullNameWithAssemblyName()}'!");
}
}
}

15
framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonModule.cs

@ -1,4 +1,8 @@
using Volo.Abp.Json.Newtonsoft;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
using Volo.Abp.Timing;
@ -9,6 +13,15 @@ namespace Volo.Abp.Json
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.TryAddEnumerable(ServiceDescriptor
.Transient<IConfigureOptions<AbpSystemTextJsonSerializerOptions>, AbpSystemTextJsonSerializerOptionsSetup>());
Configure<AbpJsonOptions>(options =>
{
options.Providers.Add<AbpNewtonsoftJsonSerializerProvider>();
options.Providers.Add<AbpSystemTextJsonSerializerProvider>();
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{
options.Converters.Add<AbpJsonIsoDateTimeConverter>();

13
framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonOptions.cs

@ -1,4 +1,6 @@
namespace Volo.Abp.Json
using Volo.Abp.Collections;
namespace Volo.Abp.Json
{
public class AbpJsonOptions
{
@ -6,5 +8,12 @@
/// Used to set default value for the DateTimeFormat.
/// </summary>
public string DefaultDateTimeFormat { get; set; }
public ITypeList<IJsonSerializerProvider> Providers { get; }
public AbpJsonOptions()
{
Providers = new TypeList<IJsonSerializerProvider>();
}
}
}
}

15
framework/src/Volo.Abp.Json/Volo/Abp/Json/IJsonSerializerProvider.cs

@ -0,0 +1,15 @@
using System;
namespace Volo.Abp.Json
{
public interface IJsonSerializerProvider
{
bool CanHandle(Type type);
string Serialize(object obj, bool camelCase = true, bool indented = false);
T Deserialize<T>(string jsonString, bool camelCase = true);
object Deserialize(Type type, string jsonString, bool camelCase = true);
}
}

9
framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs → framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerProvider.cs

@ -9,14 +9,14 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.Newtonsoft
{
public class NewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency
public class AbpNewtonsoftJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
{
private static readonly CamelCaseExceptDictionaryKeysResolver SharedCamelCaseExceptDictionaryKeysResolver =
new CamelCaseExceptDictionaryKeysResolver();
protected List<JsonConverter> Converters { get; }
public NewtonsoftJsonSerializer(
public AbpNewtonsoftJsonSerializerProvider(
IOptions<AbpNewtonsoftJsonSerializerOptions> options,
IServiceProvider serviceProvider)
{
@ -26,6 +26,11 @@ namespace Volo.Abp.Json.Newtonsoft
.ToList();
}
public bool CanHandle(Type type)
{
return true;
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)
{
return JsonConvert.SerializeObject(obj, CreateSerializerSettings(camelCase, indented));

23
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptions.cs

@ -0,0 +1,23 @@
using System.Text.Json;
using Volo.Abp.Collections;
namespace Volo.Abp.Json.SystemTextJson
{
public class AbpSystemTextJsonSerializerOptions
{
public JsonSerializerOptions JsonSerializerOptions { get; }
public ITypeList UnsupportedTypes { get; }
public AbpSystemTextJsonSerializerOptions()
{
JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
UnsupportedTypes = new TypeList();
}
}
}

24
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs

@ -0,0 +1,24 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
namespace Volo.Abp.Json
{
public class AbpSystemTextJsonSerializerOptionsSetup : IConfigureOptions<AbpSystemTextJsonSerializerOptions>
{
protected IServiceProvider ServiceProvider { get; }
public AbpSystemTextJsonSerializerOptionsSetup(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Configure(AbpSystemTextJsonSerializerOptions options)
{
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpDateTimeConverter>());
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpNullableDateTimeConverter>());
}
}
}

59
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs

@ -0,0 +1,59 @@
using System;
using System.Text.Json;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.SystemTextJson
{
public class AbpSystemTextJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency
{
protected AbpSystemTextJsonSerializerOptions Options { get; }
protected AbpSystemTextJsonUnsupportedTypeMatcher AbpSystemTextJsonUnsupportedTypeMatcher { get; }
public AbpSystemTextJsonSerializerProvider(
IOptions<AbpSystemTextJsonSerializerOptions> options,
AbpSystemTextJsonUnsupportedTypeMatcher abpSystemTextJsonUnsupportedTypeMatcher)
{
AbpSystemTextJsonUnsupportedTypeMatcher = abpSystemTextJsonUnsupportedTypeMatcher;
Options = options.Value;
}
public bool CanHandle(Type type)
{
return !AbpSystemTextJsonUnsupportedTypeMatcher.Match(type);
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)
{
return JsonSerializer.Serialize(obj, CreateJsonSerializerOptions(camelCase, indented));
}
public T Deserialize<T>(string jsonString, bool camelCase = true)
{
return JsonSerializer.Deserialize<T>(jsonString, CreateJsonSerializerOptions(camelCase));
}
public object Deserialize(Type type, string jsonString, bool camelCase = true)
{
return JsonSerializer.Deserialize(jsonString, type, CreateJsonSerializerOptions(camelCase));
}
protected virtual JsonSerializerOptions CreateJsonSerializerOptions(bool camelCase = true, bool indented = false)
{
var settings = new JsonSerializerOptions(Options.JsonSerializerOptions);
if (camelCase)
{
settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
}
if (indented)
{
settings.WriteIndented = true;
}
return settings;
}
}
}

21
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonUnsupportedTypeMatcher.cs

@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Json.SystemTextJson
{
public class AbpSystemTextJsonUnsupportedTypeMatcher : ITransientDependency
{
protected AbpSystemTextJsonSerializerOptions Options { get; }
public AbpSystemTextJsonUnsupportedTypeMatcher(IOptions<AbpSystemTextJsonSerializerOptions> options)
{
Options = options.Value;
}
public virtual bool Match(Type type)
{
return Options.UnsupportedTypes.Contains(type);
}
}
}

38
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs

@ -0,0 +1,38 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
public class AbpDateTimeConverter : JsonConverter<DateTime>, ITransientDependency
{
private readonly IClock _clock;
private readonly AbpJsonOptions _options;
public AbpDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> abpJsonOptions)
{
_clock = clock;
_options = abpJsonOptions.Value;
}
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return _clock.Normalize(reader.GetDateTime());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
if (_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
{
writer.WriteStringValue(_clock.Normalize(value));
}
else
{
writer.WriteStringValue(_clock.Normalize(value).ToString(_options.DefaultDateTimeFormat));
}
}
}
}

50
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs

@ -0,0 +1,50 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
public class AbpNullableDateTimeConverter : JsonConverter<DateTime?>, ITransientDependency
{
private readonly IClock _clock;
private readonly AbpJsonOptions _options;
public AbpNullableDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> abpJsonOptions)
{
_clock = clock;
_options = abpJsonOptions.Value;
}
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TryGetDateTime(out var dateTime))
{
return _clock.Normalize(dateTime);
}
return null;
}
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
}
else
{
if (_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
{
writer.WriteStringValue(_clock.Normalize(value.Value));
}
else
{
writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.DefaultDateTimeFormat));
}
}
}
}
}

57
framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/ObjectToInferredTypesConverter.cs

@ -0,0 +1,57 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
/// <summary>
/// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#deserialize-inferred-types-to-object-properties
/// </summary>
public class ObjectToInferredTypesConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out var l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out var datetime))
{
return datetime;
}
return reader.GetString();
}
// Use JsonElement as fallback.
// Newtonsoft uses JArray or JObject.
using (var document = JsonDocument.ParseValue(ref reader))
{
return document.RootElement.Clone();
}
}
public override void Write(Utf8JsonWriter writer, object objectToWrite, JsonSerializerOptions options)
{
throw new InvalidOperationException("Should not get here.");
}
}
}

4
framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj

@ -19,10 +19,6 @@
<EmbeddedResource Include="Volo\Abp\Localization\Resources\**\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Settings\Volo.Abp.Settings.csproj" />

21
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationDictionaryBuilder.cs

@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Volo.Abp.Localization.Json
{
public static class JsonLocalizationDictionaryBuilder
{
private static readonly JsonSerializerSettings SharedJsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
/// <summary>
/// Builds an <see cref="JsonLocalizationDictionaryBuilder" /> from given file.
/// </summary>
@ -39,8 +33,15 @@ namespace Volo.Abp.Localization.Json
JsonLocalizationFile jsonFile;
try
{
jsonFile = JsonConvert.DeserializeObject<JsonLocalizationFile>(
jsonString, SharedJsonSerializerSettings);
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
jsonFile = JsonSerializer.Deserialize<JsonLocalizationFile>(jsonString, options);
}
catch (JsonException ex)
{
@ -80,4 +81,4 @@ namespace Volo.Abp.Localization.Json
return new StaticLocalizationDictionary(cultureCode, dictionary);
}
}
}
}

4
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Json/JsonLocalizationFile.cs

@ -9,11 +9,11 @@ namespace Volo.Abp.Localization.Json
/// </summary>
public string Culture { get; set; }
public Dictionary<string, string> Texts { get; }
public Dictionary<string, string> Texts { get; set; }
public JsonLocalizationFile()
{
Texts = new Dictionary<string, string>();
}
}
}
}

41
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializer.cs

@ -1,52 +1,27 @@
using System;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text.Json;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public class Utf8JsonMemoryDbSerializer : IMemoryDbSerializer, ITransientDependency
{
private static readonly JsonSerializerSettings MemoryDbSerializerSettings;
protected Utf8JsonMemoryDbSerializerOptions Options { get; }
static Utf8JsonMemoryDbSerializer()
public Utf8JsonMemoryDbSerializer(IOptions<Utf8JsonMemoryDbSerializerOptions> options)
{
MemoryDbSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new ResolverWithPrivateSetters(),
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
Options = options.Value;
}
byte[] IMemoryDbSerializer.Serialize(object obj)
{
var jsonString = JsonConvert.SerializeObject(obj, MemoryDbSerializerSettings);
return Encoding.UTF8.GetBytes(jsonString);
return JsonSerializer.SerializeToUtf8Bytes(obj, Options.JsonSerializerOptions);
}
public object Deserialize(byte[] value, Type type)
{
var jsonString = Encoding.UTF8.GetString(value);
return JsonConvert.DeserializeObject(jsonString, type, MemoryDbSerializerSettings);
}
public class ResolverWithPrivateSetters : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (prop.Writable)
{
return prop;
}
prop.Writable = member.As<PropertyInfo>()?.GetSetMethod(true) != null;
return prop;
}
return JsonSerializer.Deserialize(value, type, Options.JsonSerializerOptions);
}
}
}
}

14
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializerOptions.cs

@ -0,0 +1,14 @@
using System.Text.Json;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public class Utf8JsonMemoryDbSerializerOptions
{
public JsonSerializerOptions JsonSerializerOptions { get; }
public Utf8JsonMemoryDbSerializerOptions()
{
JsonSerializerOptions = new JsonSerializerOptions();
}
}
}

1
framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo/Abp/AspNetCore/Authentication/OAuth/Claims/MultipleClaimAction_Tests.cs

@ -1,7 +1,6 @@
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using Newtonsoft.Json.Linq;
using Shouldly;
using Volo.Abp.Security.Claims;
using Xunit;

10
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController.cs

@ -39,6 +39,16 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
input.Time3.Value.Kind.ToString().ToLower() + "_" +
input.InnerModel.Time4.Kind.ToString().ToLower();
}
//JSON input and output.
[HttpPost("ComplexTypeDateTimeKind_JSON")]
public string ComplexTypeDateTimeKind_JSON([FromBody]GetDateTimeKindModel input)
{
return input.Time1.Kind.ToString().ToLower() + "_" +
input.Time2.Kind.ToString().ToLower() + "_" +
input.Time3.Value.Kind.ToString().ToLower() + "_" +
input.InnerModel.Time4.Kind.ToString().ToLower();
}
}
public class GetDateTimeKindModel

43
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs

@ -1,9 +1,14 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Shouldly;
using Volo.Abp.Http;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Timing;
using Xunit;
@ -13,6 +18,15 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
protected DateTimeKind DateTimeKind { get; set; }
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services.Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.UnsupportedTypes.Add<GetDateTimeKindModel>();
options.UnsupportedTypes.Add<GetDateTimeKindModel.GetDateTimeKindInnerModel>();
});
}
[Fact]
public async Task DateTimeKind_Test()
{
@ -75,8 +89,29 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
var resultAsString = await response.Content.ReadAsStringAsync();
//Time parameter(2010-01-01T00:00:00Z) with time zone information, so the default Kind is UTC
//https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50?view=aspnetcore-3.1&tabs=visual-studio#datetime-values-are-model-bound-as-utc-times
resultAsString.ShouldBe(
$"utc_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_utc");
resultAsString.ShouldBe($"utc_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_utc");
}
[Fact]
public async Task ComplexTypeDateTimeKind_JSON_Test()
{
var time = DateTime.Parse("2010-01-01T00:00:00Z");
var response = await Client.PostAsync("/api/model-Binding-test/ComplexTypeDateTimeKind_JSON",
new StringContent(JsonSerializer.Serialize(
new GetDateTimeKindModel {
Time1 = time,
Time2 = time,
Time3 = time,
InnerModel = new GetDateTimeKindModel.GetDateTimeKindInnerModel
{
Time4 = time
}
}
), Encoding.UTF8, MimeTypes.Application.Json));
response.StatusCode.ShouldBe(HttpStatusCode.OK);
var resultAsString = await response.Content.ReadAsStringAsync();
resultAsString.ShouldBe($"local_{DateTimeKind.ToString().ToLower()}_{DateTimeKind.ToString().ToLower()}_local");
}
}
@ -86,6 +121,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
DateTimeKind = DateTimeKind.Utc;
services.Configure<AbpClockOptions>(x => x.Kind = DateTimeKind);
base.ConfigureServices(context, services);
}
}
@ -95,6 +132,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
DateTimeKind = DateTimeKind.Local;
services.Configure<AbpClockOptions>(x => x.Kind = DateTimeKind);
base.ConfigureServices(context, services);
}
}
}

14
framework/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpAspNetCoreTestBase.cs

@ -1,10 +1,8 @@
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Shouldly;
using Volo.Abp.AspNetCore.TestBase;
@ -18,16 +16,10 @@ namespace Volo.Abp.AspNetCore
public abstract class AbpAspNetCoreTestBase<TStartup> : AbpAspNetCoreIntegratedTestBase<TStartup>
where TStartup : class
{
private static readonly JsonSerializerSettings SharedJsonSerializerSettings =
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
protected virtual async Task<T> GetResponseAsObjectAsync<T>(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode);
return JsonConvert.DeserializeObject<T>(strResponse, SharedJsonSerializerSettings);
return JsonSerializer.Deserialize<T>(strResponse, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
}
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
@ -49,4 +41,4 @@ namespace Volo.Abp.AspNetCore
}
}
}
}
}

67
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/JsonNetAuditSerializer_Test.cs

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace Volo.Abp.Auditing
{
public class JsonNetAuditSerializer_Test : AbpAuditingTestBase
{
private readonly JsonNetAuditSerializer _jsonNetAuditSerializer;
public JsonNetAuditSerializer_Test()
{
_jsonNetAuditSerializer = GetRequiredService<JsonNetAuditSerializer>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpAuditingOptions>(options =>
{
options.IgnoredTypes.Add(typeof(DateTime));
});
base.AfterAddApplication(services);
}
[Fact]
public void Serialize_Test()
{
var arguments = new Dictionary<string, object>
{
{"input", new InputDto {PersonData = "IdCard:123123"}},
{"input2", new Input2Dto {UserName = "admin", Password = "1q2w3E*", Birthday = DateTime.Now}}
};
var str = _jsonNetAuditSerializer.Serialize(arguments);
str.ShouldNotContain("IdCard");
str.ShouldNotContain("1q2w3E*");
str.ShouldNotContain("Birthday");
str.ShouldContain("UserName");
str.ShouldContain("admin");
}
[DisableAuditing]
class InputDto
{
public string PersonData { get; set; }
}
class Input2Dto
{
public string UserName { get; set; }
[DisableAuditing]
public string Password { get; set; }
[Newtonsoft.Json.JsonIgnore]
public string PrivateEmail { get; set; }
public DateTime Birthday { get; set; }
}
}
}

16
framework/test/Volo.Abp.Json.Tests/Volo.Abp.Json.Tests.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<ProjectReference Include="..\..\src\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
</ItemGroup>
</Project>

125
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpHybridJsonSerializer_Tests.cs

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Json.SystemTextJson;
using Xunit;
using JsonSerializer = Newtonsoft.Json.JsonSerializer;
namespace Volo.Abp.Json
{
public class AbpHybridJsonSerializer_Tests : AbpJsonTestBase
{
private readonly IJsonSerializer _jsonSerializer;
public AbpHybridJsonSerializer_Tests()
{
_jsonSerializer = GetRequiredService<IJsonSerializer>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.UnsupportedTypes.Add<MyClass1>();
options.JsonSerializerOptions.Converters.Add(new SystemTextJsonConverter());
});
services.Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{
options.Converters.Add<NewtonsoftJsonConverter>();
});
}
[Fact]
public void NewtonsoftSerialize_Test()
{
var json = _jsonSerializer.Serialize(new MyClass1
{
Providers = new List<MyClass3>
{
new MyClass3()
}
});
json.ShouldContain("Newtonsoft");
}
[Fact]
public void SystemTextJsonSerialize_Test()
{
var json = _jsonSerializer.Serialize(new MyClass2
{
Providers = new List<MyClass3>
{
new MyClass3()
}
});
json.ShouldContain("SystemTextJson");
}
public class MyClass1
{
public string Provider { get; set; }
public List<MyClass3> Providers { get; set; }
}
public class MyClass2
{
public string Provider { get; set; }
public List<MyClass3> Providers { get; set; }
}
public class MyClass3
{
public string Provider { get; set; }
}
class NewtonsoftJsonConverter : JsonConverter<MyClass1>, ITransientDependency
{
public override void WriteJson(JsonWriter writer, MyClass1 value, JsonSerializer serializer)
{
value.Provider = "Newtonsoft";
foreach (var provider in value.Providers)
{
provider.Provider = "Newtonsoft";
}
writer.WriteRawValue(JsonConvert.SerializeObject(value));
}
public override MyClass1 ReadJson(JsonReader reader, Type objectType, MyClass1 existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return (MyClass1)serializer.Deserialize(reader, objectType);
}
}
class SystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<MyClass2>, ITransientDependency
{
public override void Write(Utf8JsonWriter writer, MyClass2 value, JsonSerializerOptions options)
{
value.Provider = "SystemTextJson";
foreach (var provider in value.Providers)
{
provider.Provider = "SystemTextJson";
}
System.Text.Json.JsonSerializer.Serialize(writer, value);
}
public override MyClass2 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return (MyClass2)System.Text.Json.JsonSerializer.Deserialize(ref reader, typeToConvert);
}
}
}
}

12
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestBase.cs

@ -0,0 +1,12 @@
using Volo.Abp.Testing;
namespace Volo.Abp.Json
{
public abstract class AbpJsonTestBase : AbpIntegratedTest<AbpJsonTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

15
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpJsonTestModule.cs

@ -0,0 +1,15 @@
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.Json
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpJsonModule),
typeof(AbpTestBaseModule)
)]
public class AbpJsonTestModule : AbpModule
{
}
}

66
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonUnsupportedTypeMatcher_Tests.cs

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Json.SystemTextJson;
using Xunit;
namespace Volo.Abp.Json
{
public class AbpSystemTextJsonUnsupportedTypeMatcher_Tests : AbpJsonTestBase
{
private readonly AbpSystemTextJsonUnsupportedTypeMatcher _abpSystemTextJsonUnsupportedTypeMatcher;
public AbpSystemTextJsonUnsupportedTypeMatcher_Tests()
{
_abpSystemTextJsonUnsupportedTypeMatcher = GetRequiredService<AbpSystemTextJsonUnsupportedTypeMatcher>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.UnsupportedTypes.Add<MyClass>();
options.UnsupportedTypes.Add<byte[]>();
options.UnsupportedTypes.Add<Dictionary<string, MyClass4>>();
});
}
[Fact]
public void Match_Test()
{
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(MyClass)).ShouldBeTrue();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(byte[])).ShouldBeTrue();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(MyClass2)).ShouldBeFalse();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(MyClass3)).ShouldBeFalse();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(MyClass4)).ShouldBeFalse();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(string)).ShouldBeFalse();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(string[])).ShouldBeFalse();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(Dictionary<string, MyClass4>)).ShouldBeTrue();
_abpSystemTextJsonUnsupportedTypeMatcher.Match(typeof(IDictionary<string, MyClass4>)).ShouldBeFalse();
}
class MyClass
{
public DateTime Prop1 { get; set; }
}
class MyClass2
{
public DateTime Prop1 { get; set; }
}
class MyClass3
{
public MyClass4 Prop1 { get; set; }
}
class MyClass4
{
public DateTime Prop1 { get; set; }
}
}
}

12
framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/AbpMemoryDbTestModule.cs

@ -4,14 +4,17 @@ using Volo.Abp.Modularity;
using Volo.Abp.TestApp.MemoryDb;
using Volo.Abp.Data;
using Volo.Abp.Autofac;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.MemoryDb;
using Volo.Abp.MemoryDb.JsonConverters;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.MemoryDb
{
[DependsOn(
typeof(TestAppModule),
typeof(AbpMemoryDbModule),
typeof(TestAppModule),
typeof(AbpMemoryDbModule),
typeof(AbpAutofacModule))]
public class AbpMemoryDbTestModule : AbpModule
{
@ -29,6 +32,11 @@ namespace Volo.Abp.MemoryDb
options.AddDefaultRepositories();
options.AddRepository<City, CityRepository>();
});
Configure<Utf8JsonMemoryDbSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.Add(new EntityJsonConverter<EntityWithIntPk, int>());
});
}
}
}

33
framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs

@ -0,0 +1,33 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.MemoryDb.JsonConverters
{
public class EntityJsonConverter<TEntity, TKey> : JsonConverter<TEntity>
{
public override TEntity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var jsonDocument = JsonDocument.ParseValue(ref reader);
var entity = (TEntity)JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText(), typeToConvert);
var idJsonElement = jsonDocument.RootElement.GetProperty(nameof(IEntity<object>.Id));
var id = JsonSerializer.Deserialize<TKey>(idJsonElement.GetRawText());
if (id != null)
{
EntityHelper.TrySetId(entity, id);
}
return entity;
}
public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options)
{
var newOptions = new JsonSerializerOptions(options);
newOptions.Converters.Remove(this);
var entityConverter = (JsonConverter<TEntity>)newOptions.GetConverter(typeof(TEntity));
entityConverter.Write(writer, value, newOptions);
}
}
}

1
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests_With_Int_Pk.cs

@ -1,4 +1,5 @@
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Domain.Repositories;

4
modules/docs/src/Volo.Docs.Domain.Shared/Volo.Docs.Domain.Shared.csproj

@ -13,5 +13,9 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

14
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs

@ -1,5 +1,8 @@
using Volo.Abp.Application;
using Volo.Abp.Json.Newtonsoft;
using System.Collections.Generic;
using Volo.Abp.Application;
using Volo.Abp.FeatureManagement.JsonConverters;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
@ -7,7 +10,8 @@ namespace Volo.Abp.FeatureManagement
{
[DependsOn(
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpDddApplicationModule)
typeof(AbpDddApplicationModule),
typeof(AbpJsonModule)
)]
public class AbpFeatureManagementApplicationContractsModule : AbpModule
{
@ -18,9 +22,9 @@ namespace Volo.Abp.FeatureManagement
options.FileSets.AddEmbedded<AbpFeatureManagementApplicationContractsModule>();
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.Converters.Add<StringValueTypeJsonConverter>();
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter());
});
}
}

17
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/JsonSerializerOptionsHelper.cs

@ -0,0 +1,17 @@
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;
}
}
}

32
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters
{
public class SelectionStringValueItemSourceJsonConverter : JsonConverter<ISelectionStringValueItemSource>
{
public override ISelectionStringValueItemSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
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 selectionStringValueItem =
JsonSerializer.Deserialize<List<LocalizableSelectionStringValueItem>>(items, newOptions) ??
new List<LocalizableSelectionStringValueItem>();
return new StaticSelectionStringValueItemSource(selectionStringValueItem.ToArray());
}
public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
}
}
}

31
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs

@ -0,0 +1,31 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters
{
public class StringValueTypeJsonConverter : JsonConverter<IStringValueType>
{
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 newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), new SelectionStringValueItemSourceJsonConverter());
return name switch
{
"SelectionStringValueType" => JsonSerializer.Deserialize<SelectionStringValueType>(rootElement.GetRawText(), newOptions),
"FreeTextStringValueType" => JsonSerializer.Deserialize<FreeTextStringValueType>(rootElement.GetRawText(), newOptions),
"ToggleStringValueType" => JsonSerializer.Deserialize<ToggleStringValueType>(rootElement.GetRawText(), newOptions),
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
};
}
public override void Write(Utf8JsonWriter writer, IStringValueType value, JsonSerializerOptions options)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
}
}
}

51
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters
{
public class ValueValidatorJsonConverter : JsonConverter<IValueValidator>
{
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 valueValidator = CreateValueValidatorByName(name);
var propertiesRawText = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals("Properties", StringComparison.InvariantCultureIgnoreCase)).Value.GetRawText();
var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter());
var properties = JsonSerializer.Deserialize<IDictionary<string, object>>(propertiesRawText, newOptions);
if (properties != null && properties.Any())
{
foreach (var property in properties)
{
valueValidator.Properties[property.Key] = property.Value;
}
}
return valueValidator;
}
public override void Write(Utf8JsonWriter writer, IValueValidator value, JsonSerializerOptions options)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
}
protected virtual IValueValidator CreateValueValidatorByName(string name)
{
return name switch
{
"NULL" => new AlwaysValidValueValidator(),
"BOOLEAN" => new BooleanValueValidator(),
"NUMERIC" => new NumericValueValidator(),
"STRING" => new StringValueValidator(),
_ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
};
}
}
}

90
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/StringValueTypeJsonConverter.cs

@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement
{
public class StringValueTypeJsonConverter : JsonConverter, ITransientDependency
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IStringValueType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("This method should not be called to write (since CanWrite is false).");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var jsonObject = JObject.Load(reader);
var stringValue = CreateStringValueTypeByName(jsonObject, jsonObject["name"].ToString());
foreach (var o in serializer.Deserialize<Dictionary<string, object>>(
new JsonTextReader(new StringReader(jsonObject["properties"].ToString()))))
{
stringValue[o.Key] = o.Value;
}
stringValue.Validator = CreateValueValidatorByName(jsonObject["validator"], jsonObject["validator"]["name"].ToString());
foreach (var o in serializer.Deserialize<Dictionary<string, object>>(
new JsonTextReader(new StringReader(jsonObject["validator"]["properties"].ToString()))))
{
stringValue.Validator[o.Key] = o.Value;
}
return stringValue;
}
protected virtual IStringValueType CreateStringValueTypeByName(JObject jObject, string name)
{
if (name == "SelectionStringValueType")
{
var selectionStringValueType = new SelectionStringValueType();
if (jObject["itemSource"].HasValues)
{
selectionStringValueType.ItemSource = new StaticSelectionStringValueItemSource(jObject["itemSource"]["items"]
.Select(item => new LocalizableSelectionStringValueItem()
{
Value = item["value"].ToString(),
DisplayText = new LocalizableStringInfo(item["displayText"]["resourceName"].ToString(), item["displayText"]["name"].ToString())
}).ToArray());
}
return selectionStringValueType;
}
return name switch
{
"FreeTextStringValueType" => new FreeTextStringValueType(),
"ToggleStringValueType" => new ToggleStringValueType(),
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
};
}
protected virtual IValueValidator CreateValueValidatorByName(JToken jObject, string name)
{
return name switch
{
"NULL" => new AlwaysValidValueValidator(),
"BOOLEAN" => new BooleanValueValidator(),
"NUMERIC" => new NumericValueValidator(),
"STRING" => new StringValueValidator(),
_ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
};
}
}
}

10
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs

@ -1,9 +1,12 @@
using Localization.Resources.AbpUi;
using System.Collections.Generic;
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.FeatureManagement.JsonConverters;
namespace Volo.Abp.FeatureManagement
{
@ -28,6 +31,11 @@ namespace Volo.Abp.FeatureManagement
.Get<AbpFeatureManagementResource>()
.AddBaseTypes(typeof(AbpUiResource));
});
Configure<JsonOptions>(options =>
{
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter());
});
}
}
}

4
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.Json;
using Shouldly;
using Volo.Abp.Json;
using Volo.Abp.Validation.StringValues;
@ -67,8 +67,10 @@ namespace Volo.Abp.FeatureManagement
};
var serialized = _jsonSerializer.Serialize(featureListDto, indented: true);
var featureListDto2 = _jsonSerializer.Deserialize<GetFeatureListResultDto>(serialized);
featureListDto2.ShouldNotBeNull();
featureListDto2.Groups[0].Features[0].ValueType.ShouldBeOfType<FreeTextStringValueType>();
featureListDto2.Groups[0].Features[0].ValueType.Validator.ShouldBeOfType<BooleanValueValidator>();

9
templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs

@ -1,10 +1,8 @@
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Shouldly;
using Volo.Abp.AspNetCore.TestBase;
@ -22,10 +20,7 @@ namespace MyCompanyName.MyProjectName
protected virtual async Task<T> GetResponseAsObjectAsync<T>(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode);
return JsonConvert.DeserializeObject<T>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
return JsonSerializer.Deserialize<T>(strResponse, new JsonSerializerOptions(JsonSerializerDefaults.Web));
}
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)

Loading…
Cancel
Save