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; |
||||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|
||||
using Microsoft.Extensions.Options; |
using Microsoft.Extensions.Options; |
||||
using Microsoft.Extensions.ObjectPool; |
using Volo.Abp.Json.SystemTextJson; |
||||
using Volo.Abp.Json; |
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
namespace Volo.Abp.AspNetCore.Mvc.Json; |
||||
|
|
||||
public static class MvcCoreBuilderExtensions |
public static class MvcCoreBuilderExtensions |
||||
{ |
{ |
||||
public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder) |
public static IMvcCoreBuilder AddAbpJson(this IMvcCoreBuilder builder) |
||||
{ |
{ |
||||
var abpJsonOptions = builder.Services.ExecutePreConfiguredActions<AbpJsonOptions>(); |
builder.Services.AddOptions<JsonOptions>() |
||||
if (!abpJsonOptions.UseHybridSerializer) |
.Configure<IServiceProvider>((options, serviceProvider) => |
||||
{ |
{ |
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; |
||||
builder.AddNewtonsoftJson(); |
options.JsonSerializerOptions.AllowTrailingCommas = true; |
||||
return builder; |
|
||||
} |
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; |
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 Newtonsoft.Json; |
||||
using Volo.Abp.Collections; |
|
||||
|
|
||||
namespace Volo.Abp.Json.Newtonsoft; |
namespace Volo.Abp.Json.Newtonsoft; |
||||
|
|
||||
public class AbpNewtonsoftJsonSerializerOptions |
public class AbpNewtonsoftJsonSerializerOptions |
||||
{ |
{ |
||||
public ITypeList<JsonConverter> Converters { get; } |
public JsonSerializerSettings JsonSerializerSettings { get; } |
||||
|
|
||||
public AbpNewtonsoftJsonSerializerOptions() |
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 Volo.Abp.Json.SystemTextJson; |
||||
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.Modularity; |
||||
using Volo.Abp.Timing; |
|
||||
|
|
||||
namespace Volo.Abp.Json; |
namespace Volo.Abp.Json; |
||||
|
|
||||
[DependsOn(typeof(AbpTimingModule))] |
[DependsOn(typeof(AbpJsonSystemTextJsonModule))] |
||||
public class AbpJsonModule : AbpModule |
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.Json; |
||||
|
using Volo.Abp.Validation.StringValues; |
||||
|
using Xunit; |
||||
|
|
||||
namespace Volo.Abp.FeatureManagement; |
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