Browse Source

Introduce `IncludeNonPublicPropertiesModifiers`.

pull/13357/head
maliming 4 years ago
parent
commit
1e9c0871a3
No known key found for this signature in database GPG Key ID: 96224957E51C89E
  1. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpMvcNewtonsoftJsonOptionsSetup.cs
  2. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs
  3. 11
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpSystemTextJsonFormatter.cs
  4. 19
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpCamelCasePropertyNamesContractResolver.cs
  5. 59
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerProvider.cs
  6. 1
      framework/src/Volo.Abp.Json.SystemTextJson/Volo.Abp.Json.SystemTextJson.csproj
  7. 16
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpDefaultJsonTypeInfoResolver.cs
  8. 21
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerModifiersOptions.cs
  9. 2
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerOptionsSetup.cs
  10. 9
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonSerializerProvider.cs
  11. 21
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpSystemTextJsonUnsupportedTypeMatcher.cs
  12. 37
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/Modifiers/IncludeNonPublicPropertiesModifiers.cs
  13. 2
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs
  14. 65
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonUnsupportedTypeMatcher_Tests.cs
  15. 19
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/ExtensibleObjectModifiers_Tests.cs

16
framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpMvcNewtonsoftJsonOptionsSetup.cs

@ -1,21 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Volo.Abp.Json.Newtonsoft;
namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson;
public class AbpMvcNewtonsoftJsonOptionsSetup : IConfigureOptions<MvcNewtonsoftJsonOptions>
{
protected IServiceProvider ServiceProvider { get; }
private readonly IServiceProvider _serviceProvider;
public AbpMvcNewtonsoftJsonOptionsSetup(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
_serviceProvider = serviceProvider;
}
public void Configure(MvcNewtonsoftJsonOptions options)
{
options.SerializerSettings.ContractResolver = ServiceProvider.GetRequiredService<AbpNewtonsoftJsonContractResolver>();
options.SerializerSettings.ContractResolver = _serviceProvider.GetRequiredService<AbpCamelCasePropertyNamesContractResolver>();
var converters = _serviceProvider.GetRequiredService<IOptions<AbpNewtonsoftJsonSerializerOptions>>().Value
.Converters.Select(converterType => _serviceProvider.GetRequiredService(converterType).As<JsonConverter>())
.ToList();
options.SerializerSettings.Converters.InsertRange(0, converters);
}
}

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

@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Json.SystemTextJson.JsonConverters;
namespace Volo.Abp.AspNetCore.Mvc.Json;
@ -29,5 +30,7 @@ public class AbpJsonOptionsSetup : IConfigureOptions<JsonOptions>
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(ServiceProvider.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>());
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpSystemTextJsonFormatter.cs

@ -13,22 +13,23 @@ namespace Volo.Abp.AspNetCore.Mvc.Json;
public class AbpSystemTextJsonFormatter : IAbpHybridJsonInputFormatter, IAbpHybridJsonOutputFormatter, ITransientDependency
{
private readonly AbpSystemTextJsonUnsupportedTypeMatcher _unsupportedTypeMatcher;
private readonly IOptions<JsonOptions> _jsonOptions;
private readonly IOptions<AbpSystemTextJsonSerializerOptions> _systemTextJsonSerializerOptions;
private readonly ILoggerFactory _loggerFactory;
public AbpSystemTextJsonFormatter(AbpSystemTextJsonUnsupportedTypeMatcher unsupportedTypeMatcher,
public AbpSystemTextJsonFormatter(
IOptions<JsonOptions> jsonOptions,
IOptions<AbpSystemTextJsonSerializerOptions> systemTextJsonSerializerOptions,
ILoggerFactory loggerFactory)
{
_unsupportedTypeMatcher = unsupportedTypeMatcher;
_jsonOptions = jsonOptions;
_systemTextJsonSerializerOptions = systemTextJsonSerializerOptions;
_loggerFactory = loggerFactory;
}
Task<bool> IAbpHybridJsonInputFormatter.CanHandleAsync(Type type)
{
return Task.FromResult(!_unsupportedTypeMatcher.Match(type));
return Task.FromResult(!_systemTextJsonSerializerOptions.Value.UnsupportedTypes.Contains(type));
}
public virtual Task<TextInputFormatter> GetTextInputFormatterAsync()
@ -40,7 +41,7 @@ public class AbpSystemTextJsonFormatter : IAbpHybridJsonInputFormatter, IAbpHybr
Task<bool> IAbpHybridJsonOutputFormatter.CanHandleAsync(Type type)
{
return Task.FromResult(!_unsupportedTypeMatcher.Match(type));
return Task.FromResult(!_systemTextJsonSerializerOptions.Value.UnsupportedTypes.Contains(type));
}
public Task<TextOutputFormatter> GetTextOutputFormatterAsync()

19
framework/src/Volo.Abp.AspNetCore.Mvc.NewtonsoftJson/Volo/Abp/AspNetCore/Mvc/NewtonsoftJson/AbpNewtonsoftJsonContractResolver.cs → framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpCamelCasePropertyNamesContractResolver.cs

@ -4,17 +4,16 @@ 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.NewtonsoftJson;
namespace Volo.Abp.Json.Newtonsoft;
public class AbpNewtonsoftJsonContractResolver : DefaultContractResolver, ITransientDependency
public class AbpCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver, ITransientDependency
{
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter;
public AbpNewtonsoftJsonContractResolver(IServiceProvider serviceProvider)
public AbpCamelCasePropertyNamesContractResolver(IServiceProvider serviceProvider)
{
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>(
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>,
@ -35,7 +34,8 @@ public class AbpNewtonsoftJsonContractResolver : DefaultContractResolver, ITrans
protected virtual void ModifyProperty(MemberInfo member, JsonProperty property)
{
if (property.PropertyType != typeof(DateTime) && property.PropertyType != typeof(DateTime?))
if (property.PropertyType != typeof(DateTime) &&
property.PropertyType != typeof(DateTime?))
{
return;
}
@ -45,4 +45,13 @@ public class AbpNewtonsoftJsonContractResolver : DefaultContractResolver, ITrans
property.Converter = _dateTimeConverter.Value;
}
}
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
var contract = base.CreateDictionaryContract(objectType);
contract.DictionaryKeyResolver = propertyName => propertyName;
return contract;
}
}

59
framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerProvider.cs

@ -1,25 +1,23 @@
using System;
using System.Collections.Concurrent;
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 IServiceProvider ServiceProvider{ get; }
protected List<JsonConverter> Converters { get; }
public AbpNewtonsoftJsonSerializerProvider(
IOptions<AbpNewtonsoftJsonSerializerOptions> options,
IServiceProvider serviceProvider)
IServiceProvider serviceProvider,
IOptions<AbpNewtonsoftJsonSerializerOptions> options)
{
ServiceProvider = serviceProvider;
Converters = options.Value
.Converters
.Select(c => (JsonConverter)serviceProvider.GetRequiredService(c))
@ -33,47 +31,44 @@ public class AbpNewtonsoftJsonSerializerProvider : IJsonSerializerProvider, ITra
public string Serialize(object obj, bool camelCase = true, bool indented = false)
{
return JsonConvert.SerializeObject(obj, CreateSerializerSettings(camelCase, indented));
return JsonConvert.SerializeObject(obj, CreateJsonSerializerOptions(camelCase, indented));
}
public T Deserialize<T>(string jsonString, bool camelCase = true)
{
return JsonConvert.DeserializeObject<T>(jsonString, CreateSerializerSettings(camelCase));
return JsonConvert.DeserializeObject<T>(jsonString, CreateJsonSerializerOptions(camelCase));
}
public object Deserialize(Type type, string jsonString, bool camelCase = true)
{
return JsonConvert.DeserializeObject(jsonString, type, CreateSerializerSettings(camelCase));
return JsonConvert.DeserializeObject(jsonString, type, CreateJsonSerializerOptions(camelCase));
}
protected virtual JsonSerializerSettings CreateSerializerSettings(bool camelCase = true, bool indented = false)
{
var settings = new JsonSerializerSettings();
private readonly static ConcurrentDictionary<object, JsonSerializerSettings> JsonSerializerOptionsCache = new ConcurrentDictionary<object, JsonSerializerSettings>();
settings.Converters.InsertRange(0, Converters);
if (camelCase)
protected virtual JsonSerializerSettings CreateJsonSerializerOptions(bool camelCase = true, bool indented = false)
{
return JsonSerializerOptionsCache.GetOrAdd(new
{
settings.ContractResolver = SharedCamelCaseExceptDictionaryKeysResolver;
}
if (indented)
camelCase,
indented
}, _ =>
{
settings.Formatting = Formatting.Indented;
}
var settings = new JsonSerializerSettings();
return settings;
}
settings.Converters.InsertRange(0, Converters);
private class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
var contract = base.CreateDictionaryContract(objectType);
if (camelCase)
{
settings.ContractResolver = ServiceProvider.GetRequiredService<AbpCamelCasePropertyNamesContractResolver>();
}
contract.DictionaryKeyResolver = propertyName => propertyName;
if (indented)
{
settings.Formatting = Formatting.Indented;
}
return contract;
}
return settings;
});
}
}

1
framework/src/Volo.Abp.Json.SystemTextJson/Volo.Abp.Json.SystemTextJson.csproj

@ -17,6 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Json.Core\Volo.Abp.Json.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
<PackageReference Include="System.Text.Json" Version="$(MicrosoftAspNetCorePackageVersion)" />
</ItemGroup>
</Project>

16
framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/AbpDefaultJsonTypeInfoResolver.cs

@ -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);
}
}
}

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

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization.Metadata;
using Volo.Abp.Data;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.Json.SystemTextJson;
public class AbpSystemTextJsonSerializerModifiersOptions
{
public List<Action<JsonTypeInfo>> Modifiers { get; }
public AbpSystemTextJsonSerializerModifiersOptions()
{
Modifiers = new List<Action<JsonTypeInfo>>
{
new IncludeNonPublicPropertiesModifiers<ExtensibleObject, ExtraPropertyDictionary>().CreateModifyAction("ExtraProperties")
};
}
}

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

@ -28,5 +28,7 @@ public class AbpSystemTextJsonSerializerOptionsSetup : IConfigureOptions<AbpSyst
// 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.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(ServiceProvider.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>());
}
}

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

@ -10,19 +10,14 @@ public class AbpSystemTextJsonSerializerProvider : IJsonSerializerProvider, ITra
{
protected AbpSystemTextJsonSerializerOptions Options { get; }
protected AbpSystemTextJsonUnsupportedTypeMatcher AbpSystemTextJsonUnsupportedTypeMatcher { get; }
public AbpSystemTextJsonSerializerProvider(
IOptions<AbpSystemTextJsonSerializerOptions> options,
AbpSystemTextJsonUnsupportedTypeMatcher abpSystemTextJsonUnsupportedTypeMatcher)
public AbpSystemTextJsonSerializerProvider(IOptions<AbpSystemTextJsonSerializerOptions> options)
{
AbpSystemTextJsonUnsupportedTypeMatcher = abpSystemTextJsonUnsupportedTypeMatcher;
Options = options.Value;
}
public bool CanHandle(Type type)
{
return !AbpSystemTextJsonUnsupportedTypeMatcher.Match(type);
return !Options.UnsupportedTypes.Contains(type);
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)

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

@ -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);
}
}

37
framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/Modifiers/IncludeNonPublicPropertiesModifiers.cs

@ -0,0 +1,37 @@
using System;
using System.Linq;
using System.Text.Json.Serialization.Metadata;
namespace Volo.Abp.Json.SystemTextJson.Modifiers;
public class IncludeNonPublicPropertiesModifiers<TClass, TProperty>
where TClass : class
{
private string _propertyName;
public Action<JsonTypeInfo> CreateModifyAction(string propertyName)
{
_propertyName = propertyName;
return Modify;
}
public void Modify(JsonTypeInfo jsonTypeInfo)
{
if (jsonTypeInfo.Type == typeof(TClass))
{
var propertyJsonInfo = jsonTypeInfo.Properties.FirstOrDefault(x => x.Name.Equals(_propertyName, StringComparison.OrdinalIgnoreCase) && x.Set == null);
if (propertyJsonInfo != null)
{
var propertyInfo = typeof(TClass).GetProperty(_propertyName);
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);
}
}
}
}
}

2
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.DynamicProxy;
@ -10,7 +9,6 @@ namespace Volo.Abp.ObjectExtending;
[Serializable]
public class ExtensibleObject : IHasExtraProperties, IValidatableObject
{
[JsonInclude]
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public ExtensibleObject()

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

@ -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; }
}
}

19
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/ExtensibleObjectModifiers_Tests.cs

@ -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<AbpSystemTextJsonSerializerProvider>();
var extensibleObject = jsonSerializer.Deserialize<ExtensibleObject>("{\"ExtraProperties\": {\"Test-Key\":\"Test-Value\"}}");
extensibleObject.ExtraProperties.ShouldContainKeyAndValue("Test-Key", "Test-Value");
}
}
Loading…
Cancel
Save