mirror of https://github.com/abpframework/abp.git
committed by
GitHub
93 changed files with 1347 additions and 1293 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,29 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc> |
|||
<AssemblyName>Volo.Abp.AspNetCore.Mvc.NewtonsoftJson</AssemblyName> |
|||
<PackageId>Volo.Abp.AspNetCore.Mvc.NewtonsoftJson</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<IsPackable>true</IsPackable> |
|||
<OutputType>Library</OutputType> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Newtonsoft\Volo.Abp.Json.Newtonsoft.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(MicrosoftAspNetCorePackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.Newtonsoft; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; |
|||
|
|||
[DependsOn(typeof(AbpJsonNewtonsoftModule), typeof(AbpAspNetCoreMvcModule))] |
|||
public class AbpAspNetCoreMvcNewtonsoftModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddMvcCore().AddNewtonsoftJson(); |
|||
|
|||
context.Services.AddOptions<MvcNewtonsoftJsonOptions>() |
|||
.Configure<AbpCamelCasePropertyNamesContractResolver>((options, contractResolver) => |
|||
{ |
|||
options.SerializerSettings.ContractResolver = contractResolver; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
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 |
|||
{ |
|||
private readonly SystemTextJsonInputFormatter _systemTextJsonInputFormatter; |
|||
private readonly NewtonsoftJsonInputFormatter _newtonsoftJsonInputFormatter; |
|||
|
|||
public AbpHybridJsonInputFormatter(SystemTextJsonInputFormatter systemTextJsonInputFormatter, NewtonsoftJsonInputFormatter newtonsoftJsonInputFormatter) |
|||
{ |
|||
_systemTextJsonInputFormatter = systemTextJsonInputFormatter; |
|||
_newtonsoftJsonInputFormatter = newtonsoftJsonInputFormatter; |
|||
|
|||
SupportedEncodings.Add(UTF8EncodingWithoutBOM); |
|||
SupportedEncodings.Add(UTF16EncodingLittleEndian); |
|||
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); |
|||
} |
|||
|
|||
public override async 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 _systemTextJsonInputFormatter; |
|||
} |
|||
|
|||
return _newtonsoftJsonInputFormatter; |
|||
} |
|||
|
|||
public virtual InputFormatterExceptionPolicy ExceptionPolicy => InputFormatterExceptionPolicy.MalformedInputExceptions; |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
using System.Buffers; |
|||
using System.Text.Encodings.Web; |
|||
using System.Text.Json; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Formatters; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpHybridJsonOptionsSetup : IConfigureOptions<MvcOptions> |
|||
{ |
|||
private readonly IOptions<JsonOptions> _jsonOptions; |
|||
private readonly IOptions<MvcNewtonsoftJsonOptions> _mvcNewtonsoftJsonOptions; |
|||
private readonly ILoggerFactory _loggerFactory; |
|||
private readonly ArrayPool<char> _charPool; |
|||
private readonly ObjectPoolProvider _objectPoolProvider; |
|||
|
|||
public AbpHybridJsonOptionsSetup( |
|||
IOptions<JsonOptions> jsonOptions, |
|||
IOptions<MvcNewtonsoftJsonOptions> mvcNewtonsoftJsonOptions, |
|||
ILoggerFactory loggerFactory, |
|||
ArrayPool<char> charPool, |
|||
ObjectPoolProvider objectPoolProvider) |
|||
{ |
|||
_jsonOptions = jsonOptions; |
|||
_mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions; |
|||
_loggerFactory = loggerFactory; |
|||
_charPool = charPool; |
|||
_objectPoolProvider = objectPoolProvider; |
|||
} |
|||
|
|||
public void Configure(MvcOptions options) |
|||
{ |
|||
var systemTextJsonInputFormatter = new SystemTextJsonInputFormatter( |
|||
_jsonOptions.Value, |
|||
_loggerFactory.CreateLogger<SystemTextJsonInputFormatter>()); |
|||
|
|||
var newtonsoftJsonInputFormatter = new NewtonsoftJsonInputFormatter( |
|||
_loggerFactory.CreateLogger<NewtonsoftJsonInputFormatter>(), |
|||
_mvcNewtonsoftJsonOptions.Value.SerializerSettings, |
|||
_charPool, |
|||
_objectPoolProvider, |
|||
options, |
|||
_mvcNewtonsoftJsonOptions.Value); |
|||
|
|||
options.InputFormatters.RemoveType<SystemTextJsonInputFormatter>(); |
|||
options.InputFormatters.RemoveType<NewtonsoftJsonInputFormatter>(); |
|||
options.InputFormatters.Add(new AbpHybridJsonInputFormatter(systemTextJsonInputFormatter, newtonsoftJsonInputFormatter)); |
|||
|
|||
var jsonSerializerOptions = _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, |
|||
}; |
|||
} |
|||
|
|||
var systemTextJsonOutputFormatter = new SystemTextJsonOutputFormatter(jsonSerializerOptions); |
|||
var newtonsoftJsonOutputFormatter = new NewtonsoftJsonOutputFormatter( |
|||
_mvcNewtonsoftJsonOptions.Value.SerializerSettings, |
|||
_charPool, |
|||
options); |
|||
|
|||
options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>(); |
|||
options.OutputFormatters.RemoveType<NewtonsoftJsonOutputFormatter>(); |
|||
options.OutputFormatters.Add(new AbpHybridJsonOutputFormatter(systemTextJsonOutputFormatter, newtonsoftJsonOutputFormatter)); |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
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 |
|||
{ |
|||
private readonly SystemTextJsonOutputFormatter _systemTextJsonOutputFormatter; |
|||
private readonly NewtonsoftJsonOutputFormatter _newtonsoftJsonOutputFormatter; |
|||
|
|||
public AbpHybridJsonOutputFormatter(SystemTextJsonOutputFormatter systemTextJsonOutputFormatter, NewtonsoftJsonOutputFormatter newtonsoftJsonOutputFormatter) |
|||
{ |
|||
_systemTextJsonOutputFormatter = systemTextJsonOutputFormatter; |
|||
_newtonsoftJsonOutputFormatter = newtonsoftJsonOutputFormatter; |
|||
|
|||
SupportedEncodings.Add(Encoding.UTF8); |
|||
SupportedEncodings.Add(Encoding.Unicode); |
|||
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); |
|||
} |
|||
|
|||
public override async 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 _systemTextJsonOutputFormatter; |
|||
} |
|||
|
|||
return _newtonsoftJsonOutputFormatter; |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
using System; |
|||
using System.Text.Json; |
|||
using System.Text.Json.Serialization; |
|||
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>()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
} |
|||
} |
|||
@ -1,48 +0,0 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
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, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpMvcJsonContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>, |
|||
true |
|||
); |
|||
|
|||
NamingStrategy = new CamelCaseNamingStrategy(); |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
ModifyProperty(member, property); |
|||
|
|||
return property; |
|||
} |
|||
|
|||
protected virtual void ModifyProperty(MemberInfo member, JsonProperty property) |
|||
{ |
|||
if (property.PropertyType != typeof(DateTime) && property.PropertyType != typeof(DateTime?)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
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>(); |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
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(); |
|||
} |
|||
@ -1,28 +1,31 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System; |
|||
using System.Text.Json; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public static class MvcCoreBuilderExtensions |
|||
{ |
|||
public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder) |
|||
public static IMvcCoreBuilder AddAbpJson(this IMvcCoreBuilder builder) |
|||
{ |
|||
var abpJsonOptions = builder.Services.ExecutePreConfiguredActions<AbpJsonOptions>(); |
|||
if (!abpJsonOptions.UseHybridSerializer) |
|||
{ |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
|||
builder.AddNewtonsoftJson(); |
|||
return builder; |
|||
} |
|||
builder.Services.AddOptions<JsonOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; |
|||
options.JsonSerializerOptions.AllowTrailingCommas = true; |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
|
|||
options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(serviceProvider |
|||
.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>()); |
|||
}); |
|||
|
|||
builder.Services.TryAddTransient<DefaultObjectPoolProvider>(); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<JsonOptions>, AbpJsonOptionsSetup>()); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, AbpHybridJsonOptionsSetup>()); |
|||
return builder; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Volo.Abp.AspNetCore.TestBase; |
|||
|
|||
public class AbpNoopHostLifetime : IHostLifetime |
|||
{ |
|||
public Task StopAsync(CancellationToken cancellationToken) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task WaitForStartAsync(CancellationToken cancellationToken) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Hosting.Server; |
|||
using Microsoft.AspNetCore.TestHost; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Volo.Abp.AspNetCore.TestBase; |
|||
|
|||
public static class AbpWebHostBuilderExtensions |
|||
{ |
|||
public static IWebHostBuilder UseAbpTestServer(this IWebHostBuilder builder) |
|||
{ |
|||
return builder.ConfigureServices(services => |
|||
{ |
|||
services.AddScoped<IHostLifetime, AbpNoopHostLifetime>(); |
|||
services.AddScoped<IServer, TestServer>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
public class AuditingContractResolver : CamelCasePropertyNamesContractResolver |
|||
{ |
|||
private readonly List<Type> _ignoredTypes; |
|||
|
|||
public AuditingContractResolver(List<Type> ignoredTypes) |
|||
{ |
|||
_ignoredTypes = ignoredTypes; |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (_ignoredTypes.Any(ignoredType => ignoredType.GetTypeInfo().IsAssignableFrom(property.PropertyType))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
return property; |
|||
} |
|||
|
|||
if (member.DeclaringType != null && (member.DeclaringType.IsDefined(typeof(DisableAuditingAttribute)) || member.DeclaringType.IsDefined(typeof(JsonIgnoreAttribute)))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
return property; |
|||
} |
|||
|
|||
if (member.IsDefined(typeof(DisableAuditingAttribute)) || member.IsDefined(typeof(JsonIgnoreAttribute))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
using System.Collections.Concurrent; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Text.Json; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
public class JsonAuditSerializer : IAuditSerializer, ITransientDependency |
|||
{ |
|||
protected AbpAuditingOptions Options; |
|||
|
|||
public JsonAuditSerializer(IOptions<AbpAuditingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public string Serialize(object obj) |
|||
{ |
|||
return JsonSerializer.Serialize(obj, CreateJsonSerializerOptions()); |
|||
} |
|||
|
|||
private static readonly ConcurrentDictionary<string, JsonSerializerOptions> JsonSerializerOptionsCache = |
|||
new ConcurrentDictionary<string, JsonSerializerOptions>(); |
|||
|
|||
protected virtual JsonSerializerOptions CreateJsonSerializerOptions() |
|||
{ |
|||
return JsonSerializerOptionsCache.GetOrAdd(nameof(JsonAuditSerializer), _ => |
|||
{ |
|||
var settings = new JsonSerializerOptions() |
|||
{ |
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, |
|||
TypeInfoResolver = new DefaultJsonTypeInfoResolver() |
|||
{ |
|||
Modifiers = |
|||
{ |
|||
jsonTypeInfo => |
|||
{ |
|||
if (Options.IgnoredTypes.Any(ignoredType => ignoredType.IsAssignableFrom(jsonTypeInfo.Type)) || |
|||
jsonTypeInfo.Type.GetCustomAttributes(typeof(DisableAuditingAttribute), false).Any()) |
|||
{ |
|||
if (jsonTypeInfo.Kind == JsonTypeInfoKind.Object) |
|||
{ |
|||
jsonTypeInfo.Properties.Clear(); |
|||
} |
|||
} |
|||
|
|||
foreach (var property in jsonTypeInfo.Properties) |
|||
{ |
|||
if (Options.IgnoredTypes.Any(ignoredType => ignoredType.IsAssignableFrom(property.PropertyType))) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
|
|||
if (property.AttributeProvider != null && |
|||
property.AttributeProvider.GetCustomAttributes(typeof(DisableAuditingAttribute), false).Any()) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
|
|||
if (property.PropertyType.DeclaringType != null && |
|||
property.PropertyType.DeclaringType.IsDefined(typeof(DisableAuditingAttribute))) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
return settings; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,43 +0,0 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
//TODO: Rename to JsonAuditSerializer
|
|||
public class JsonNetAuditSerializer : IAuditSerializer, ITransientDependency |
|||
{ |
|||
protected AbpAuditingOptions Options; |
|||
|
|||
public JsonNetAuditSerializer(IOptions<AbpAuditingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public string Serialize(object obj) |
|||
{ |
|||
return JsonConvert.SerializeObject(obj, GetSharedJsonSerializerSettings()); |
|||
} |
|||
|
|||
private static readonly object SyncObj = new object(); |
|||
private static JsonSerializerSettings _sharedJsonSerializerSettings; |
|||
|
|||
private JsonSerializerSettings GetSharedJsonSerializerSettings() |
|||
{ |
|||
if (_sharedJsonSerializerSettings == null) |
|||
{ |
|||
lock (SyncObj) |
|||
{ |
|||
if (_sharedJsonSerializerSettings == null) |
|||
{ |
|||
_sharedJsonSerializerSettings = new JsonSerializerSettings |
|||
{ |
|||
ContractResolver = new AuditingContractResolver(Options.IgnoredTypes) |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return _sharedJsonSerializerSettings; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<PackageId>Volo.Abp.Json.Abstractions</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpJsonAbstractionsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpJsonOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Formats of input JSON date, Empty string means default format.
|
|||
/// </summary>
|
|||
public List<string> InputDateTimeFormats { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Format of output json date, Null or empty string means default format.
|
|||
/// </summary>
|
|||
public string OutputDateTimeFormat { get; set; } |
|||
|
|||
public AbpJsonOptions() |
|||
{ |
|||
InputDateTimeFormats = new List<string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Json.Newtonsoft</AssemblyName> |
|||
<PackageId>Volo.Abp.Json.Newtonsoft</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Abstractions\Volo.Abp.Json.Abstractions.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" /> |
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpCamelCasePropertyNamesContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpDateTimeConverter>, |
|||
true |
|||
); |
|||
|
|||
NamingStrategy = new CamelCaseNamingStrategy |
|||
{ |
|||
ProcessDictionaryKeys = false |
|||
}; |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (AbpDateTimeConverter.ShouldNormalize(member, property)) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Converters; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpDateTimeConverter : DateTimeConverterBase |
|||
{ |
|||
private readonly string _dateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; |
|||
private readonly DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind; |
|||
private readonly CultureInfo _culture = CultureInfo.InvariantCulture; |
|||
private readonly IClock _clock; |
|||
private readonly AbpJsonOptions _options; |
|||
|
|||
public AbpDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> options) |
|||
{ |
|||
_clock = clock; |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public override bool CanConvert(Type objectType) |
|||
{ |
|||
return objectType == typeof(DateTime) || objectType == typeof(DateTime?); |
|||
} |
|||
|
|||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) |
|||
{ |
|||
var nullable = Nullable.GetUnderlyingType(objectType) != null; |
|||
if (reader.TokenType == JsonToken.Null) |
|||
{ |
|||
if (!nullable) |
|||
{ |
|||
throw new JsonSerializationException($"Cannot convert null value to {objectType.FullName}."); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
if (reader.TokenType == JsonToken.Date) |
|||
{ |
|||
return _clock.Normalize(reader.Value.To<DateTime>()); |
|||
} |
|||
|
|||
if (reader.TokenType != JsonToken.String) |
|||
{ |
|||
throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}."); |
|||
} |
|||
|
|||
var dateText = reader.Value?.ToString(); |
|||
|
|||
if (dateText.IsNullOrEmpty() && nullable) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
if (_options.InputDateTimeFormats.Any()) |
|||
{ |
|||
foreach (var format in _options.InputDateTimeFormats) |
|||
{ |
|||
if (DateTime.TryParseExact(dateText, format, _culture, _dateTimeStyles, out var d1)) |
|||
{ |
|||
return _clock.Normalize(d1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var date = !_dateTimeFormat.IsNullOrEmpty() ? |
|||
DateTime.ParseExact(dateText, _dateTimeFormat, _culture, _dateTimeStyles) : |
|||
DateTime.Parse(dateText, _culture, _dateTimeStyles); |
|||
|
|||
return _clock.Normalize(date); |
|||
} |
|||
|
|||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
value = _clock.Normalize(value.To<DateTime>()); |
|||
} |
|||
|
|||
if (value is DateTime dateTime) |
|||
{ |
|||
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal || |
|||
(_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) |
|||
{ |
|||
dateTime = dateTime.ToUniversalTime(); |
|||
} |
|||
|
|||
writer.WriteValue(_options.OutputDateTimeFormat.IsNullOrWhiteSpace() |
|||
? dateTime.ToString(_dateTimeFormat, _culture) |
|||
: dateTime.ToString(_options.OutputDateTimeFormat, _culture)); |
|||
} |
|||
else |
|||
{ |
|||
throw new JsonSerializationException($"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}."); |
|||
} |
|||
} |
|||
|
|||
internal static bool ShouldNormalize(MemberInfo member, JsonProperty property) |
|||
{ |
|||
if (property.PropertyType != typeof(DateTime) && |
|||
property.PropertyType != typeof(DateTime?)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpDefaultContractResolver : DefaultContractResolver, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpDefaultContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpDateTimeConverter>, |
|||
true |
|||
); |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (AbpDateTimeConverter.ShouldNormalize(member, property)) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
[DependsOn(typeof(AbpJsonAbstractionsModule), typeof(AbpTimingModule))] |
|||
public class AbpJsonNewtonsoftModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddOptions<AbpNewtonsoftJsonSerializerOptions>() |
|||
.Configure<AbpCamelCasePropertyNamesContractResolver>((options, contractResolver) => |
|||
{ |
|||
options.JsonSerializerSettings.ContractResolver = contractResolver; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,96 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class AbpNewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected IOptions<AbpNewtonsoftJsonSerializerOptions> Options { get; } |
|||
|
|||
public AbpNewtonsoftJsonSerializer(IServiceProvider serviceProvider, IOptions<AbpNewtonsoftJsonSerializerOptions> options) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options; |
|||
} |
|||
|
|||
public string Serialize(object obj, bool camelCase = true, bool indented = false) |
|||
{ |
|||
return JsonConvert.SerializeObject(obj, CreateJsonSerializerOptions(camelCase, indented)); |
|||
} |
|||
|
|||
public T Deserialize<T>(string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject<T>(jsonString, CreateJsonSerializerOptions(camelCase)); |
|||
} |
|||
|
|||
public object Deserialize(Type type, string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject(jsonString, type, CreateJsonSerializerOptions(camelCase)); |
|||
} |
|||
|
|||
private static readonly ConcurrentDictionary<object, JsonSerializerSettings> JsonSerializerOptionsCache = |
|||
new ConcurrentDictionary<object, JsonSerializerSettings>(); |
|||
|
|||
protected virtual JsonSerializerSettings CreateJsonSerializerOptions(bool camelCase = true, bool indented = false) |
|||
{ |
|||
return JsonSerializerOptionsCache.GetOrAdd(new |
|||
{ |
|||
camelCase, |
|||
indented |
|||
}, _ => |
|||
{ |
|||
var settings = new JsonSerializerSettings |
|||
{ |
|||
Binder = Options.Value.JsonSerializerSettings.Binder, |
|||
CheckAdditionalContent = Options.Value.JsonSerializerSettings.CheckAdditionalContent, |
|||
Context = Options.Value.JsonSerializerSettings.Context, |
|||
ContractResolver = Options.Value.JsonSerializerSettings.ContractResolver, |
|||
ConstructorHandling = Options.Value.JsonSerializerSettings.ConstructorHandling, |
|||
Converters = Options.Value.JsonSerializerSettings.Converters, |
|||
Culture = Options.Value.JsonSerializerSettings.Culture, |
|||
DateFormatHandling = Options.Value.JsonSerializerSettings.DateFormatHandling, |
|||
DateFormatString = Options.Value.JsonSerializerSettings.DateFormatString, |
|||
DateParseHandling = Options.Value.JsonSerializerSettings.DateParseHandling, |
|||
DateTimeZoneHandling = Options.Value.JsonSerializerSettings.DateTimeZoneHandling, |
|||
DefaultValueHandling = Options.Value.JsonSerializerSettings.DefaultValueHandling, |
|||
Error = Options.Value.JsonSerializerSettings.Error, |
|||
EqualityComparer = Options.Value.JsonSerializerSettings.EqualityComparer, |
|||
FloatFormatHandling = Options.Value.JsonSerializerSettings.FloatFormatHandling, |
|||
FloatParseHandling = Options.Value.JsonSerializerSettings.FloatParseHandling, |
|||
Formatting = Options.Value.JsonSerializerSettings.Formatting, |
|||
MaxDepth = Options.Value.JsonSerializerSettings.MaxDepth, |
|||
MetadataPropertyHandling = Options.Value.JsonSerializerSettings.MetadataPropertyHandling, |
|||
MissingMemberHandling = Options.Value.JsonSerializerSettings.MissingMemberHandling, |
|||
NullValueHandling = Options.Value.JsonSerializerSettings.NullValueHandling, |
|||
ObjectCreationHandling = Options.Value.JsonSerializerSettings.ObjectCreationHandling, |
|||
PreserveReferencesHandling = Options.Value.JsonSerializerSettings.PreserveReferencesHandling, |
|||
ReferenceLoopHandling = Options.Value.JsonSerializerSettings.ReferenceLoopHandling, |
|||
ReferenceResolver = Options.Value.JsonSerializerSettings.ReferenceResolver, |
|||
ReferenceResolverProvider = Options.Value.JsonSerializerSettings.ReferenceResolverProvider, |
|||
SerializationBinder = Options.Value.JsonSerializerSettings.SerializationBinder, |
|||
StringEscapeHandling = Options.Value.JsonSerializerSettings.StringEscapeHandling, |
|||
TraceWriter = Options.Value.JsonSerializerSettings.TraceWriter, |
|||
TypeNameAssemblyFormat = Options.Value.JsonSerializerSettings.TypeNameAssemblyFormat, |
|||
TypeNameHandling = Options.Value.JsonSerializerSettings.TypeNameHandling, |
|||
TypeNameAssemblyFormatHandling = Options.Value.JsonSerializerSettings.TypeNameAssemblyFormatHandling |
|||
}; |
|||
|
|||
settings.ContractResolver = camelCase |
|||
? ServiceProvider.GetRequiredService<AbpCamelCasePropertyNamesContractResolver>() |
|||
: ServiceProvider.GetRequiredService<AbpDefaultContractResolver>(); |
|||
|
|||
if (indented) |
|||
{ |
|||
settings.Formatting = Formatting.Indented; |
|||
} |
|||
|
|||
return settings; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,14 +1,13 @@ |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpNewtonsoftJsonSerializerOptions |
|||
{ |
|||
public ITypeList<JsonConverter> Converters { get; } |
|||
public JsonSerializerSettings JsonSerializerSettings { get; } |
|||
|
|||
public AbpNewtonsoftJsonSerializerOptions() |
|||
{ |
|||
Converters = new TypeList<JsonConverter>(); |
|||
JsonSerializerSettings = new JsonSerializerSettings(); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Json.SystemTextJson</AssemblyName> |
|||
<PackageId>Volo.Abp.Json.SystemTextJson</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Abstractions\Volo.Abp.Json.Abstractions.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" /> |
|||
<PackageReference Include="System.Text.Json" Version="$(MicrosoftAspNetCorePackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,16 @@ |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
public class AbpDefaultJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, ITransientDependency |
|||
{ |
|||
public AbpDefaultJsonTypeInfoResolver(IOptions<AbpSystemTextJsonSerializerModifiersOptions> options) |
|||
{ |
|||
foreach (var modifier in options.Value.Modifiers) |
|||
{ |
|||
Modifiers.Add(modifier); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System; |
|||
using System.Text.Encodings.Web; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
[DependsOn(typeof(AbpJsonAbstractionsModule), typeof(AbpTimingModule))] |
|||
public class AbpJsonSystemTextJsonModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddOptions<AbpSystemTextJsonSerializerOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
|
|||
options.JsonSerializerOptions.Encoder ??= JavaScriptEncoder.UnsafeRelaxedJsonEscaping; |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
|
|||
options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(serviceProvider |
|||
.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>()); |
|||
}); |
|||
|
|||
context.Services.AddOptions<AbpSystemTextJsonSerializerModifiersOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
options.Modifiers.Add(new AbpDateTimeConverterModifier().CreateModifyAction(serviceProvider)); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
public class AbpSystemTextJsonSerializerModifiersOptions |
|||
{ |
|||
public List<Action<JsonTypeInfo>> Modifiers { get; } |
|||
|
|||
public AbpSystemTextJsonSerializerModifiersOptions() |
|||
{ |
|||
Modifiers = new List<Action<JsonTypeInfo>> |
|||
{ |
|||
AbpIncludeExtraPropertiesModifiers.Modify, |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public class AbpDateTimeConverterModifier |
|||
{ |
|||
private IServiceProvider _serviceProvider; |
|||
|
|||
public Action<JsonTypeInfo> CreateModifyAction(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
return Modify; |
|||
} |
|||
|
|||
private void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (ReflectionHelper.GetAttributesOfMemberOrDeclaringType<DisableDateTimeNormalizationAttribute>(jsonTypeInfo.Type).Any()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var property in jsonTypeInfo.Properties.Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?))) |
|||
{ |
|||
if (property.AttributeProvider == null || |
|||
!property.AttributeProvider.GetCustomAttributes(typeof(DisableDateTimeNormalizationAttribute), false).Any()) |
|||
{ |
|||
property.CustomConverter = property.PropertyType == typeof(DateTime) |
|||
? _serviceProvider.GetRequiredService<AbpDateTimeConverter>() |
|||
: _serviceProvider.GetRequiredService<AbpNullableDateTimeConverter>(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq.Expressions; |
|||
using System.Reflection; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public class AbpIgnorePropertiesModifiers<TClass, TProperty> |
|||
where TClass : class |
|||
{ |
|||
private Expression<Func<TClass, TProperty>> _propertySelector; |
|||
|
|||
public Action<JsonTypeInfo> CreateModifyAction(Expression<Func<TClass, TProperty>> propertySelector) |
|||
{ |
|||
_propertySelector = propertySelector; |
|||
return Modify; |
|||
} |
|||
|
|||
public void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (jsonTypeInfo.Type == typeof(TClass)) |
|||
{ |
|||
jsonTypeInfo.Properties.RemoveAll( |
|||
x => x.AttributeProvider is MemberInfo memberInfo && |
|||
memberInfo.Name == _propertySelector.Body.As<MemberExpression>().Member.Name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public static class AbpIncludeExtraPropertiesModifiers |
|||
{ |
|||
public static void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
var propertyJsonInfo = jsonTypeInfo.Properties |
|||
.Where(x => x.AttributeProvider is MemberInfo) |
|||
.FirstOrDefault(x => |
|||
x.PropertyType == typeof(ExtraPropertyDictionary) && |
|||
x.AttributeProvider.As<MemberInfo>().Name == nameof(ExtensibleObject.ExtraProperties) && |
|||
x.Set == null); |
|||
|
|||
if (propertyJsonInfo != null) |
|||
{ |
|||
propertyJsonInfo.Set = (extraProperties, value) => |
|||
{ |
|||
ObjectHelper.TrySetProperty(extraProperties.As<ExtensibleObject>(), x => x.ExtraProperties, () => (ExtraPropertyDictionary)value); |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Reflection; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public class AbpIncludeNonPublicPropertiesModifiers<TClass, TProperty> |
|||
where TClass : class |
|||
{ |
|||
private Expression<Func<TClass, TProperty>> _propertySelector; |
|||
|
|||
public Action<JsonTypeInfo> CreateModifyAction(Expression<Func<TClass, TProperty>> propertySelector) |
|||
{ |
|||
_propertySelector = propertySelector; |
|||
return Modify; |
|||
} |
|||
|
|||
public void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (jsonTypeInfo.Type == typeof(TClass)) |
|||
{ |
|||
var propertyName = _propertySelector.Body.As<MemberExpression>().Member.Name; |
|||
var propertyJsonInfo = jsonTypeInfo.Properties.FirstOrDefault(x => |
|||
x.AttributeProvider is MemberInfo memberInfo && |
|||
memberInfo.Name == propertyName && |
|||
x.Set == null); |
|||
if (propertyJsonInfo != null) |
|||
{ |
|||
var propertyInfo = typeof(TClass).GetProperty(propertyName, BindingFlags.NonPublic); |
|||
if (propertyInfo != null) |
|||
{ |
|||
var jsonPropertyInfo = jsonTypeInfo.CreateJsonPropertyInfo(typeof(TProperty), propertyJsonInfo.Name); |
|||
jsonPropertyInfo.Get = propertyInfo.GetValue; |
|||
jsonPropertyInfo.Set = propertyInfo.SetValue; |
|||
jsonTypeInfo.Properties.Remove(propertyJsonInfo); |
|||
jsonTypeInfo.Properties.Add(jsonPropertyInfo); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,66 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using JetBrains.Annotations; |
|||
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([CanBeNull] 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>([NotNull] string jsonString, bool camelCase = true) |
|||
{ |
|||
Check.NotNull(jsonString, nameof(jsonString)); |
|||
|
|||
using (var scope = ServiceScopeFactory.CreateScope()) |
|||
{ |
|||
var serializerProvider = GetSerializerProvider(scope.ServiceProvider, typeof(T)); |
|||
return serializerProvider.Deserialize<T>(jsonString, camelCase); |
|||
} |
|||
} |
|||
|
|||
public object Deserialize(Type type, [NotNull] string jsonString, bool camelCase = true) |
|||
{ |
|||
Check.NotNull(jsonString, nameof(jsonString)); |
|||
|
|||
using (var scope = ServiceScopeFactory.CreateScope()) |
|||
{ |
|||
var serializerProvider = GetSerializerProvider(scope.ServiceProvider, type); |
|||
return serializerProvider.Deserialize(type, jsonString, camelCase); |
|||
} |
|||
} |
|||
|
|||
protected virtual IJsonSerializerProvider GetSerializerProvider(IServiceProvider serviceProvider, [CanBeNull] 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()}'!"); |
|||
} |
|||
} |
|||
@ -1,34 +1,10 @@ |
|||
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.Json.SystemTextJson; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
[DependsOn(typeof(AbpTimingModule))] |
|||
[DependsOn(typeof(AbpJsonSystemTextJsonModule))] |
|||
public class AbpJsonModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.TryAddEnumerable(ServiceDescriptor |
|||
.Transient<IConfigureOptions<AbpSystemTextJsonSerializerOptions>, AbpSystemTextJsonSerializerOptionsSetup>()); |
|||
|
|||
var preActions = context.Services.GetPreConfigureActions<AbpJsonOptions>(); |
|||
Configure<AbpJsonOptions>(options => |
|||
{ |
|||
options.Providers.Add<AbpNewtonsoftJsonSerializerProvider>(); |
|||
if (preActions.Configure().UseHybridSerializer) |
|||
{ |
|||
options.Providers.Add<AbpSystemTextJsonSerializerProvider>(); |
|||
} |
|||
}); |
|||
|
|||
Configure<AbpNewtonsoftJsonSerializerOptions>(options => |
|||
{ |
|||
options.Converters.Add<AbpJsonIsoDateTimeConverter>(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@ -1,27 +0,0 @@ |
|||
using Volo.Abp.Collections; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpJsonOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Used to set default value for the DateTimeFormat.
|
|||
/// </summary>
|
|||
public string DefaultDateTimeFormat { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// It will try to use System.Json.Text to handle JSON if it can otherwise use Newtonsoft.
|
|||
/// Affects both AbpJsonModule and AbpAspNetCoreMvcModule.
|
|||
/// See <see cref="AbpSystemTextJsonUnsupportedTypeMatcher"/>
|
|||
/// </summary>
|
|||
public bool UseHybridSerializer { get; set; } |
|||
|
|||
public ITypeList<IJsonSerializerProvider> Providers { get; } |
|||
|
|||
public AbpJsonOptions() |
|||
{ |
|||
Providers = new TypeList<IJsonSerializerProvider>(); |
|||
UseHybridSerializer = true; |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public interface IJsonSerializerProvider |
|||
{ |
|||
bool CanHandle([CanBeNull] 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); |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
using System; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Converters; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpJsonIsoDateTimeConverter : IsoDateTimeConverter, ITransientDependency |
|||
{ |
|||
private readonly IClock _clock; |
|||
|
|||
public AbpJsonIsoDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> abpJsonOptions) |
|||
{ |
|||
_clock = clock; |
|||
|
|||
if (abpJsonOptions.Value.DefaultDateTimeFormat != null) |
|||
{ |
|||
DateTimeFormat = abpJsonOptions.Value.DefaultDateTimeFormat; |
|||
} |
|||
} |
|||
|
|||
public override bool CanConvert(Type objectType) |
|||
{ |
|||
if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) |
|||
{ |
|||
var date = base.ReadJson(reader, objectType, existingValue, serializer) as DateTime?; |
|||
|
|||
if (date.HasValue) |
|||
{ |
|||
return _clock.Normalize(date.Value); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) |
|||
{ |
|||
var date = value as DateTime?; |
|||
base.WriteJson(writer, date.HasValue ? _clock.Normalize(date.Value) : value, serializer); |
|||
} |
|||
} |
|||
@ -1,79 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpNewtonsoftJsonSerializerProvider : IJsonSerializerProvider, ITransientDependency |
|||
{ |
|||
private static readonly CamelCaseExceptDictionaryKeysResolver SharedCamelCaseExceptDictionaryKeysResolver = |
|||
new CamelCaseExceptDictionaryKeysResolver(); |
|||
|
|||
protected List<JsonConverter> Converters { get; } |
|||
|
|||
public AbpNewtonsoftJsonSerializerProvider( |
|||
IOptions<AbpNewtonsoftJsonSerializerOptions> options, |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
Converters = options.Value |
|||
.Converters |
|||
.Select(c => (JsonConverter)serviceProvider.GetRequiredService(c)) |
|||
.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)); |
|||
} |
|||
|
|||
public T Deserialize<T>(string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject<T>(jsonString, CreateSerializerSettings(camelCase)); |
|||
} |
|||
|
|||
public object Deserialize(Type type, string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject(jsonString, type, CreateSerializerSettings(camelCase)); |
|||
} |
|||
|
|||
protected virtual JsonSerializerSettings CreateSerializerSettings(bool camelCase = true, bool indented = false) |
|||
{ |
|||
var settings = new JsonSerializerSettings(); |
|||
|
|||
settings.Converters.InsertRange(0, Converters); |
|||
|
|||
if (camelCase) |
|||
{ |
|||
settings.ContractResolver = SharedCamelCaseExceptDictionaryKeysResolver; |
|||
} |
|||
|
|||
if (indented) |
|||
{ |
|||
settings.Formatting = Formatting.Indented; |
|||
} |
|||
|
|||
return settings; |
|||
} |
|||
|
|||
private class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver |
|||
{ |
|||
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) |
|||
{ |
|||
var contract = base.CreateDictionaryContract(objectType); |
|||
|
|||
contract.DictionaryKeyResolver = propertyName => propertyName; |
|||
|
|||
return contract; |
|||
} |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
using System; |
|||
using System.Text.Encodings.Web; |
|||
using System.Text.Json.Serialization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
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>()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
|
|||
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
|
|||
options.JsonSerializerOptions.Encoder ??= JavaScriptEncoder.UnsafeRelaxedJsonEscaping; |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
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([CanBeNull] Type type) |
|||
{ |
|||
return Options.UnsupportedTypes.Contains(type); |
|||
} |
|||
} |
|||
@ -1,149 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
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"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SystemTextJsonSerialize_With_Dictionary_Test() |
|||
{ |
|||
var json = _jsonSerializer.Serialize(new MyClassWithDictionary |
|||
{ |
|||
Properties = |
|||
{ |
|||
{"A", "AV"}, |
|||
{"B", "BV"} |
|||
} |
|||
}); |
|||
|
|||
var deserialized = _jsonSerializer.Deserialize<MyClassWithDictionary>(json); |
|||
deserialized.Properties.ShouldContain(p => p.Key == "A" && p.Value == "AV"); |
|||
deserialized.Properties.ShouldContain(p => p.Key == "B" && p.Value == "BV"); |
|||
} |
|||
|
|||
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; } |
|||
} |
|||
|
|||
public class MyClassWithDictionary |
|||
{ |
|||
public Dictionary<string, string> Properties { get; set; } |
|||
|
|||
public MyClassWithDictionary() |
|||
{ |
|||
Properties = new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpIgnorePropertiesModifiers_Tests : AbpJsonTestBase |
|||
{ |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public AbpIgnorePropertiesModifiers_Tests() |
|||
{ |
|||
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpSystemTextJsonSerializerModifiersOptions>(options => |
|||
{ |
|||
options.Modifiers.Add(new AbpIgnorePropertiesModifiers<FooDto, List<BarDto>>().CreateModifyAction(x => x.BarDtos)); |
|||
options.Modifiers.Add(new AbpIgnorePropertiesModifiers<BarDto, string>().CreateModifyAction(x => x.Id)); |
|||
}); |
|||
|
|||
base.AfterAddApplication(services); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Test() |
|||
{ |
|||
var json = _jsonSerializer.Serialize(new FooDto() |
|||
{ |
|||
Name = "foo", |
|||
BarDtos = new List<BarDto> |
|||
{ |
|||
new BarDto |
|||
{ |
|||
Name = "bar1" |
|||
}, |
|||
new BarDto |
|||
{ |
|||
Name = "bar2" |
|||
} |
|||
} |
|||
}); |
|||
|
|||
json.ShouldNotContain("bar"); |
|||
|
|||
json = _jsonSerializer.Serialize(new BarDto() |
|||
{ |
|||
Id = "id", |
|||
Name = "bar" |
|||
}); |
|||
|
|||
json.ShouldNotContain("id"); |
|||
} |
|||
|
|||
class FooDto |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public List<BarDto> BarDtos { get; set; } |
|||
} |
|||
|
|||
class BarDto |
|||
{ |
|||
public string Id { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpIncludeNonPublicPropertiesModifiers_Tests : AbpJsonTestBase |
|||
{ |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public AbpIncludeNonPublicPropertiesModifiers_Tests() |
|||
{ |
|||
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpSystemTextJsonSerializerModifiersOptions>(options => |
|||
{ |
|||
options.Modifiers.Add(new AbpIncludeNonPublicPropertiesModifiers<NonPublicPropertiesClass, string>().CreateModifyAction(x => x.Name)); |
|||
options.Modifiers.Add(new AbpIncludeNonPublicPropertiesModifiers<NonPublicPropertiesClass, string>().CreateModifyAction(x => x.Age)); |
|||
}); |
|||
|
|||
base.AfterAddApplication(services); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Test() |
|||
{ |
|||
var json = _jsonSerializer.Serialize(new NonPublicPropertiesClass() |
|||
{ |
|||
Id = "id" |
|||
}); |
|||
|
|||
json.ShouldContain("id"); |
|||
json.ShouldContain("name"); |
|||
json.ShouldContain("age"); |
|||
|
|||
var obj = _jsonSerializer.Deserialize<NonPublicPropertiesClass>(json); |
|||
obj.Id.ShouldBe("id"); |
|||
obj.Name.ShouldBe("name"); |
|||
obj.Age.ShouldBe("age"); |
|||
} |
|||
|
|||
class NonPublicPropertiesClass |
|||
{ |
|||
public string Id { get; set; } |
|||
|
|||
public string Name { get; private set; } |
|||
|
|||
public string Age { get; protected set; } |
|||
|
|||
public NonPublicPropertiesClass() |
|||
{ |
|||
Name = "name"; |
|||
Age = "age"; |
|||
} |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class ExtensibleObjectModifiers_Tests : AbpJsonTestBase |
|||
{ |
|||
[Fact] |
|||
public void Should_Modify_Object() |
|||
{ |
|||
var jsonSerializer = GetRequiredService<AbpSystemTextJsonSerializer>(); |
|||
|
|||
var extensibleObject = jsonSerializer.Deserialize<ExtensibleObject>("{\"ExtraProperties\": {\"Test-Key\":\"Test-Value\"}}"); |
|||
|
|||
extensibleObject.ExtraProperties.ShouldContainKeyAndValue("Test-Key", "Test-Value"); |
|||
} |
|||
} |
|||
@ -1,95 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Validation.StringValues; |
|||
|
|||
namespace Volo.Abp.FeatureManagement.JsonConverters; |
|||
|
|||
public class NewtonsoftStringValueTypeJsonConverter : JsonConverter, ITransientDependency |
|||
{ |
|||
public override bool CanWrite => false; |
|||
|
|||
protected readonly ValueValidatorFactoryOptions Options; |
|||
|
|||
public NewtonsoftStringValueTypeJsonConverter(IOptions<ValueValidatorFactoryOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
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) |
|||
{ |
|||
foreach (var factory in Options.ValueValidatorFactory.Where(factory => factory.CanCreate(name))) |
|||
{ |
|||
return factory.Create(); |
|||
} |
|||
|
|||
throw new ArgumentException($"{nameof(IValueValidator)} named {name} was cannot be created!"); |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.FeatureManagement; |
|||
|
|||
public class NewtonsoftStringValueJsonConverter_Tests : StringValueJsonConverter_Tests |
|||
{ |
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
services.PreConfigure<AbpJsonOptions>(options => |
|||
{ |
|||
options.UseHybridSerializer = false; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,122 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.FeatureManagement.JsonConverters; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Validation.StringValues; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.FeatureManagement; |
|||
|
|||
public abstract class StringValueJsonConverter_Tests : FeatureManagementApplicationTestBase |
|||
{ |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public StringValueJsonConverter_Tests() |
|||
{ |
|||
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
protected override void BeforeAddApplication(IServiceCollection services) |
|||
{ |
|||
services.PreConfigure<ValueValidatorFactoryOptions>(options => |
|||
{ |
|||
options.ValueValidatorFactory.Add(new ValueValidatorFactory<UrlValueValidator>("URL")); |
|||
}); |
|||
|
|||
base.BeforeAddApplication(services); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Serialize_And_Deserialize() |
|||
{ |
|||
var featureListDto = new GetFeatureListResultDto |
|||
{ |
|||
Groups = new List<FeatureGroupDto> |
|||
{ |
|||
new FeatureGroupDto |
|||
{ |
|||
Name = "MyGroup", |
|||
DisplayName = "MyGroup", |
|||
Features = new List<FeatureDto> |
|||
{ |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new FreeTextStringValueType |
|||
{ |
|||
Validator = new BooleanValueValidator() |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new SelectionStringValueType |
|||
{ |
|||
ItemSource = new StaticSelectionStringValueItemSource( |
|||
new LocalizableSelectionStringValueItem |
|||
{ |
|||
Value = "TestValue", |
|||
DisplayText = new LocalizableStringInfo("TestResourceName", "TestName") |
|||
}), |
|||
Validator = new AlwaysValidValueValidator() |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new ToggleStringValueType |
|||
{ |
|||
Validator = new NumericValueValidator |
|||
{ |
|||
MaxValue = 1000, |
|||
MinValue = 10 |
|||
} |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
Provider = new FeatureProviderDto |
|||
{ |
|||
Name = "FeatureName", |
|||
Key = "FeatureKey" |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new FreeTextStringValueType |
|||
{ |
|||
Validator = new UrlValueValidator("https") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
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>(); |
|||
|
|||
featureListDto2.Groups[0].Features[1].ValueType.ShouldBeOfType<SelectionStringValueType>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.Validator.ShouldBeOfType<AlwaysValidValueValidator>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x => |
|||
x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" && |
|||
x.DisplayText.Name == "TestName"); |
|||
|
|||
featureListDto2.Groups[0].Features[2].ValueType.ShouldBeOfType<ToggleStringValueType>(); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.ShouldBeOfType<NumericValueValidator>(); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MaxValue.ShouldBe(1000); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MinValue.ShouldBe(10); |
|||
|
|||
featureListDto2.Groups[0].Features[3].Provider.Name.ShouldBe("FeatureName"); |
|||
featureListDto2.Groups[0].Features[3].Provider.Key.ShouldBe("FeatureKey"); |
|||
|
|||
featureListDto2.Groups[0].Features[4].ValueType.ShouldBeOfType<FreeTextStringValueType>(); |
|||
featureListDto2.Groups[0].Features[4].ValueType.Validator.ShouldBeOfType<UrlValueValidator>(); |
|||
featureListDto2.Groups[0].Features[4].ValueType.Validator.As<UrlValueValidator>().Scheme.ShouldBe("https"); |
|||
} |
|||
} |
|||
@ -1,15 +1,122 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.FeatureManagement.JsonConverters; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Validation.StringValues; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.FeatureManagement; |
|||
|
|||
public class SystemTextJsonStringValueJsonConverter_Tests : StringValueJsonConverter_Tests |
|||
public class SystemTextJsonStringValueJsonConverter_Tests : FeatureManagementApplicationTestBase |
|||
{ |
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public SystemTextJsonStringValueJsonConverter_Tests() |
|||
{ |
|||
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
protected override void BeforeAddApplication(IServiceCollection services) |
|||
{ |
|||
services.PreConfigure<AbpJsonOptions>(options => |
|||
services.PreConfigure<ValueValidatorFactoryOptions>(options => |
|||
{ |
|||
options.UseHybridSerializer = true; |
|||
options.ValueValidatorFactory.Add(new ValueValidatorFactory<UrlValueValidator>("URL")); |
|||
}); |
|||
|
|||
base.BeforeAddApplication(services); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Serialize_And_Deserialize() |
|||
{ |
|||
var featureListDto = new GetFeatureListResultDto |
|||
{ |
|||
Groups = new List<FeatureGroupDto> |
|||
{ |
|||
new FeatureGroupDto |
|||
{ |
|||
Name = "MyGroup", |
|||
DisplayName = "MyGroup", |
|||
Features = new List<FeatureDto> |
|||
{ |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new FreeTextStringValueType |
|||
{ |
|||
Validator = new BooleanValueValidator() |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new SelectionStringValueType |
|||
{ |
|||
ItemSource = new StaticSelectionStringValueItemSource( |
|||
new LocalizableSelectionStringValueItem |
|||
{ |
|||
Value = "TestValue", |
|||
DisplayText = new LocalizableStringInfo("TestResourceName", "TestName") |
|||
}), |
|||
Validator = new AlwaysValidValueValidator() |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new ToggleStringValueType |
|||
{ |
|||
Validator = new NumericValueValidator |
|||
{ |
|||
MaxValue = 1000, |
|||
MinValue = 10 |
|||
} |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
Provider = new FeatureProviderDto |
|||
{ |
|||
Name = "FeatureName", |
|||
Key = "FeatureKey" |
|||
} |
|||
}, |
|||
new FeatureDto |
|||
{ |
|||
ValueType = new FreeTextStringValueType |
|||
{ |
|||
Validator = new UrlValueValidator("https") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
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>(); |
|||
|
|||
featureListDto2.Groups[0].Features[1].ValueType.ShouldBeOfType<SelectionStringValueType>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.Validator.ShouldBeOfType<AlwaysValidValueValidator>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>(); |
|||
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x => |
|||
x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" && |
|||
x.DisplayText.Name == "TestName"); |
|||
|
|||
featureListDto2.Groups[0].Features[2].ValueType.ShouldBeOfType<ToggleStringValueType>(); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.ShouldBeOfType<NumericValueValidator>(); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MaxValue.ShouldBe(1000); |
|||
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MinValue.ShouldBe(10); |
|||
|
|||
featureListDto2.Groups[0].Features[3].Provider.Name.ShouldBe("FeatureName"); |
|||
featureListDto2.Groups[0].Features[3].Provider.Key.ShouldBe("FeatureKey"); |
|||
|
|||
featureListDto2.Groups[0].Features[4].ValueType.ShouldBeOfType<FreeTextStringValueType>(); |
|||
featureListDto2.Groups[0].Features[4].ValueType.Validator.ShouldBeOfType<UrlValueValidator>(); |
|||
featureListDto2.Groups[0].Features[4].ValueType.Validator.As<UrlValueValidator>().Scheme.ShouldBe("https"); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue