mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-09-19 01:58:14 +08:00
refactor(tenpayv2): 优化代码
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -8,6 +9,8 @@ using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Newtonsoft.Json.Converters
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities;
|
||||
|
||||
internal static class FlattenNArrayObjectConverterBase
|
||||
{
|
||||
public const string PROPERTY_WILDCARD_NARRAY_ELEMENT = "$n";
|
||||
@@ -17,7 +20,7 @@ namespace Newtonsoft.Json.Converters
|
||||
internal abstract partial class FlattenNArrayObjectConverterBase<T> : JsonConverter<T?>
|
||||
where T : class, new()
|
||||
{
|
||||
private sealed class InnerTypedJsonProperty
|
||||
private sealed class InnerTypedJsonPropertyInfo
|
||||
{
|
||||
public string PropertyName { get; }
|
||||
|
||||
@@ -27,14 +30,17 @@ namespace Newtonsoft.Json.Converters
|
||||
|
||||
public bool PropertyIsNArray { get; }
|
||||
|
||||
public JsonConverter? JsonConverter { get; }
|
||||
public JsonConverter? JsonConverterOnRead { get; }
|
||||
|
||||
public InnerTypedJsonProperty(string propertyName, PropertyInfo propertyInfo, bool propertyIsNArray, JsonConverter? jsonConverter)
|
||||
public JsonConverter? JsonConverterOnWrite { get; }
|
||||
|
||||
public InnerTypedJsonPropertyInfo(string propertyName, PropertyInfo propertyInfo, bool propertyIsNArray, JsonConverter? jsonReadConverter, JsonConverter? jsonWriteConverter)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
PropertyInfo = propertyInfo;
|
||||
PropertyIsNArray = propertyIsNArray;
|
||||
JsonConverter = jsonConverter;
|
||||
JsonConverterOnRead = jsonReadConverter;
|
||||
JsonConverterOnWrite = jsonWriteConverter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,44 +67,47 @@ namespace Newtonsoft.Json.Converters
|
||||
}
|
||||
else if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
InnerTypedJsonProperty[] typedJsonProperties = GetTypedJsonProperties(objectType);
|
||||
InnerTypedJsonPropertyInfo[] typedJsonProperties = GetTypedJsonProperties(objectType);
|
||||
if (typedJsonProperties.Count(p => p.PropertyIsNArray) != 1)
|
||||
throw new JsonSerializationException("The number of `$n` properties must be only one.");
|
||||
|
||||
JObject jObject = JObject.Load(reader);
|
||||
T tObject = new T();
|
||||
|
||||
foreach (JProperty jKey in jObject.Properties())
|
||||
foreach (JProperty jProperty in jObject.Properties())
|
||||
{
|
||||
InnerTypedJsonProperty? typedJsonProperty = typedJsonProperties.SingleOrDefault(e => e.PropertyName == jKey.Name);
|
||||
if (typedJsonProperty != null)
|
||||
InnerTypedJsonPropertyInfo? typedJsonPropertyInfo = typedJsonProperties.SingleOrDefault(e => e.PropertyName == jProperty.Name);
|
||||
if (typedJsonPropertyInfo != null)
|
||||
{
|
||||
// 处理普通属性
|
||||
JsonSerializer tmpSerializer = serializer;
|
||||
if (typedJsonProperty.JsonConverter != null)
|
||||
JsonSerializer tmpSerializer = GetClonedJsonSerializer(serializer, typedJsonPropertyInfo.JsonConverterOnRead);
|
||||
object? propertyValue = jProperty.Value?.ToObject(typedJsonPropertyInfo.PropertyType, tmpSerializer);
|
||||
typedJsonPropertyInfo.PropertyInfo.SetValue(tObject, propertyValue);
|
||||
}
|
||||
else if (TryMatchNArrayIndex(jProperty.Name, out int index))
|
||||
{
|
||||
tmpSerializer = JsonSerializer.CreateDefault(serializer.ExtractSerializerSettings());
|
||||
tmpSerializer.Converters.Add(typedJsonProperty.JsonConverter);
|
||||
typedJsonPropertyInfo = typedJsonProperties.Single(e => e.PropertyIsNArray);
|
||||
|
||||
Array? propertyValue = typedJsonPropertyInfo.PropertyInfo.GetValue(tObject) as Array;
|
||||
ReflectionUtility.CreateOrExpandArray(ref propertyValue, typedJsonPropertyInfo.PropertyType.GetElementType()!, index + 1);
|
||||
ReflectionUtility.CreateOrExpandArrayElement(propertyValue!, index, (object element) =>
|
||||
{
|
||||
InnerTypedJsonPropertyInfo? insider = GetTypedJsonProperties(element.GetType())
|
||||
.SingleOrDefault(p => string.Equals(p.PropertyName.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, index.ToString()), jProperty.Name));
|
||||
if (insider != null)
|
||||
{
|
||||
JsonSerializer tmpSerializer = GetClonedJsonSerializer(serializer, insider.JsonConverterOnRead);
|
||||
object? elementPropertyValue = jProperty.Value?.ToObject(insider.PropertyType, tmpSerializer);
|
||||
insider.PropertyInfo.SetValue(element, elementPropertyValue);
|
||||
}
|
||||
|
||||
object? value = jObject[typedJsonProperty.PropertyName]?.ToObject(typedJsonProperty.PropertyType, tmpSerializer);
|
||||
typedJsonProperty.PropertyInfo.SetValue(tObject, value);
|
||||
}
|
||||
else if (TryMatchNArrayIndex(jKey.Name, out int index))
|
||||
{
|
||||
// 处理 $n 属性
|
||||
InnerTypedJsonProperty narrayJsonProperty = typedJsonProperties.Single(e => e.PropertyIsNArray);
|
||||
object? value = narrayJsonProperty.PropertyInfo.GetValue(tObject);
|
||||
return element;
|
||||
});
|
||||
|
||||
JsonSerializer tmpSerializer = serializer;
|
||||
|
||||
Array array = CreateOrExpandNArray(value, narrayJsonProperty.PropertyType.GetElementType()!, index + 1);
|
||||
object? element = CreateOrUpdateNArrayElement(array, index, jKey.Name, jKey.Value, tmpSerializer);
|
||||
narrayJsonProperty.PropertyInfo.SetValue(tObject, array);
|
||||
typedJsonPropertyInfo.PropertyInfo.SetValue(tObject, propertyValue);
|
||||
}
|
||||
else if (serializer.MissingMemberHandling == MissingMemberHandling.Error)
|
||||
{
|
||||
throw new JsonSerializationException($"Could not find member `{jKey.Name}` on object of type `{objectType.Name}`.");
|
||||
throw new JsonSerializationException($"Could not find member `{jProperty.Name}` on object of type `{objectType.Name}`.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,29 +128,28 @@ namespace Newtonsoft.Json.Converters
|
||||
writer.WriteStartObject();
|
||||
|
||||
JsonObjectContract jsonContract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
|
||||
foreach (JsonProperty jsonProperty in jsonContract.Properties)
|
||||
foreach (JsonProperty jsonContractProperty in jsonContract.Properties)
|
||||
{
|
||||
if (jsonProperty.Ignored)
|
||||
if (jsonContractProperty.Ignored)
|
||||
continue;
|
||||
if (!(jsonProperty.ShouldSerialize == null || jsonProperty.ShouldSerialize(value)))
|
||||
if (!(jsonContractProperty.ShouldSerialize == null || jsonContractProperty.ShouldSerialize(value)))
|
||||
continue;
|
||||
|
||||
string propertyKey = jsonProperty.PropertyName;
|
||||
object? propertyValue = jsonProperty.ValueProvider.GetValue(value);
|
||||
string propertyName = jsonContractProperty.PropertyName;
|
||||
object? propertyValue = jsonContractProperty.ValueProvider?.GetValue(value);
|
||||
if (propertyValue is null)
|
||||
{
|
||||
if (serializer.NullValueHandling == NullValueHandling.Ignore)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PROPERTY_NAME_NARRAY.Equals(propertyKey))
|
||||
if (!PROPERTY_NAME_NARRAY.Equals(propertyName))
|
||||
{
|
||||
// 处理普通属性
|
||||
writer.WritePropertyName(propertyKey);
|
||||
writer.WritePropertyName(propertyName);
|
||||
|
||||
if (jsonProperty.Converter != null && jsonProperty.Converter.CanWrite)
|
||||
if (jsonContractProperty.Converter != null && jsonContractProperty.Converter.CanWrite)
|
||||
{
|
||||
jsonProperty.Converter.WriteJson(writer, propertyValue, serializer);
|
||||
jsonContractProperty.Converter.WriteJson(writer, propertyValue, serializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -150,7 +158,6 @@ namespace Newtonsoft.Json.Converters
|
||||
}
|
||||
else
|
||||
{
|
||||
// 处理 $n 属性
|
||||
if (propertyValue is null)
|
||||
continue;
|
||||
|
||||
@@ -161,10 +168,10 @@ namespace Newtonsoft.Json.Converters
|
||||
if (jSubToken == null)
|
||||
continue;
|
||||
|
||||
foreach (JProperty jSubKey in jSubToken)
|
||||
foreach (JProperty jSubProperty in jSubToken)
|
||||
{
|
||||
writer.WritePropertyName(jSubKey.Name.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, i.ToString()));
|
||||
serializer.Serialize(writer, jSubKey.Value);
|
||||
writer.WritePropertyName(jSubProperty.Name.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, i.ToString()));
|
||||
serializer.Serialize(writer, jSubProperty.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,16 +180,16 @@ namespace Newtonsoft.Json.Converters
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
private static InnerTypedJsonProperty[] GetTypedJsonProperties(Type type)
|
||||
private static InnerTypedJsonPropertyInfo[] GetTypedJsonProperties(Type type)
|
||||
{
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
string mappedTypeKey = type.AssemblyQualifiedName ?? type.GetHashCode().ToString();
|
||||
InnerTypedJsonProperty[]? typedJsonProperties = (InnerTypedJsonProperty[]?)_mappedTypeJsonProperties[mappedTypeKey];
|
||||
string mappedKey = type.AssemblyQualifiedName ?? type.GetHashCode().ToString();
|
||||
InnerTypedJsonPropertyInfo[]? mappedValue = (InnerTypedJsonPropertyInfo[]?)_mappedTypeJsonProperties[mappedKey];
|
||||
|
||||
if (typedJsonProperties == null)
|
||||
if (mappedValue == null)
|
||||
{
|
||||
typedJsonProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
mappedValue = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(p =>
|
||||
{
|
||||
if (p.CanWrite || p.GetCustomAttributes<JsonPropertyAttribute>(inherit: true).Any())
|
||||
@@ -195,24 +202,39 @@ namespace Newtonsoft.Json.Converters
|
||||
.Select(p =>
|
||||
{
|
||||
string name = p.GetCustomAttribute<JsonPropertyAttribute>(inherit: true)?.PropertyName ?? p.Name;
|
||||
JsonConverter? converter = p.GetCustomAttributes<JsonConverterAttribute>(inherit: true)
|
||||
IEnumerable<JsonConverter> converters = p.GetCustomAttributes<JsonConverterAttribute>(inherit: true)
|
||||
.OrderByDescending(attr => attr.IsDefaultAttribute())
|
||||
.Select(attr => (JsonConverter)Activator.CreateInstance(attr.ConverterType, attr.ConverterParameters)!)
|
||||
.FirstOrDefault();
|
||||
return new InnerTypedJsonProperty
|
||||
.Select(attr => (JsonConverter)Activator.CreateInstance(attr.ConverterType, attr.ConverterParameters)!);
|
||||
JsonConverter? readConverter = converters.FirstOrDefault(c => c.CanRead);
|
||||
JsonConverter? writeConverter = converters.FirstOrDefault(c => c.CanWrite);
|
||||
return new InnerTypedJsonPropertyInfo
|
||||
(
|
||||
propertyName: name,
|
||||
propertyInfo: p,
|
||||
propertyIsNArray: PROPERTY_NAME_NARRAY.Equals(name) && p.PropertyType.IsArray && p.PropertyType.GetElementType()!.IsClass,
|
||||
jsonConverter: converter
|
||||
jsonReadConverter: readConverter,
|
||||
jsonWriteConverter: writeConverter
|
||||
);
|
||||
})
|
||||
.OrderBy(e => e.PropertyInfo.GetCustomAttribute<JsonPropertyAttribute>(inherit: true)?.Order)
|
||||
.ToArray();
|
||||
_mappedTypeJsonProperties[mappedTypeKey] = typedJsonProperties;
|
||||
_mappedTypeJsonProperties[mappedKey] = mappedValue;
|
||||
}
|
||||
|
||||
return typedJsonProperties;
|
||||
return mappedValue;
|
||||
}
|
||||
|
||||
private static JsonSerializer GetClonedJsonSerializer(JsonSerializer serializer, JsonConverter? converter)
|
||||
{
|
||||
JsonSerializer serializerCopy = serializer;
|
||||
|
||||
if (converter != null)
|
||||
{
|
||||
serializerCopy = JsonSerializer.CreateDefault(serializer.ExtractSerializerSettings());
|
||||
serializerCopy.Converters.Add(converter);
|
||||
}
|
||||
|
||||
return serializerCopy;
|
||||
}
|
||||
|
||||
private static bool TryMatchNArrayIndex(string key, out int index)
|
||||
@@ -228,70 +250,5 @@ namespace Newtonsoft.Json.Converters
|
||||
index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Array CreateOrExpandNArray(object? array, Type elementType, int capacity)
|
||||
{
|
||||
if (elementType == null) throw new ArgumentNullException(nameof(elementType));
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
|
||||
if (array == null)
|
||||
{
|
||||
return Array.CreateInstance(elementType, capacity);
|
||||
}
|
||||
|
||||
Array src = (Array)array;
|
||||
if (src.Length < capacity)
|
||||
{
|
||||
Array dst = Array.CreateInstance(elementType, capacity);
|
||||
Array.Copy(src, dst, src.Length);
|
||||
return dst;
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
private static object CreateOrUpdateNArrayElement(Array array, int index, string jKey, JToken? jValue, JsonSerializer serializer)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
object? element = array.GetValue(index);
|
||||
Type elementType = array.GetType().GetElementType()!;
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
|
||||
if (elementType.IsAbstract || elementType.IsInterface)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
else if (elementType.IsArray)
|
||||
{
|
||||
element = Array.CreateInstance(elementType, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
element = Activator.CreateInstance(elementType);
|
||||
}
|
||||
|
||||
array.SetValue(element, index);
|
||||
}
|
||||
|
||||
InnerTypedJsonProperty? typedJsonProperty = GetTypedJsonProperties(elementType)
|
||||
.SingleOrDefault(p => string.Equals(p.PropertyName.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, index.ToString()), jKey));
|
||||
if (typedJsonProperty != null)
|
||||
{
|
||||
if (typedJsonProperty.JsonConverter != null)
|
||||
{
|
||||
serializer = JsonSerializer.CreateDefault(serializer.ExtractSerializerSettings());
|
||||
serializer.Converters.Add(typedJsonProperty.JsonConverter);
|
||||
}
|
||||
|
||||
object? obj = jValue?.ToObject(typedJsonProperty.PropertyType, serializer);
|
||||
typedJsonProperty.PropertyInfo.SetValue(element, obj);
|
||||
}
|
||||
|
||||
return element!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,8 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace System.Text.Json.Converters
|
||||
{
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities;
|
||||
|
||||
internal static class FlattenNArrayObjectConverterBase
|
||||
{
|
||||
public const string PROPERTY_WILDCARD_NARRAY_ELEMENT = Newtonsoft.Json.Converters.FlattenNArrayObjectConverterBase.PROPERTY_WILDCARD_NARRAY_ELEMENT;
|
||||
@@ -17,7 +19,7 @@ namespace System.Text.Json.Converters
|
||||
internal class FlattenNArrayObjectConverterBase<T> : JsonConverter<T?>
|
||||
where T : class, new()
|
||||
{
|
||||
private sealed class InnerTypedJsonProperty
|
||||
private sealed class InnerTypedJsonPropertyInfo
|
||||
{
|
||||
public string PropertyName { get; }
|
||||
|
||||
@@ -29,7 +31,7 @@ namespace System.Text.Json.Converters
|
||||
|
||||
public JsonConverter? JsonConverter { get; }
|
||||
|
||||
public InnerTypedJsonProperty(string propertyName, PropertyInfo propertyInfo, bool propertyIsNArray, JsonConverter? jsonConverter)
|
||||
public InnerTypedJsonPropertyInfo(string propertyName, PropertyInfo propertyInfo, bool propertyIsNArray, JsonConverter? jsonConverter)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
PropertyInfo = propertyInfo;
|
||||
@@ -51,40 +53,43 @@ namespace System.Text.Json.Converters
|
||||
}
|
||||
else if (reader.TokenType == JsonTokenType.StartObject)
|
||||
{
|
||||
InnerTypedJsonProperty[] typedJsonProperties = GetTypedJsonProperties(typeToConvert);
|
||||
InnerTypedJsonPropertyInfo[] typedJsonProperties = GetTypedJsonProperties(typeToConvert);
|
||||
if (typedJsonProperties.Count(p => p.PropertyIsNArray) != 1)
|
||||
throw new JsonException("The number of `$n` properties must be only one.");
|
||||
|
||||
JsonElement jElement = JsonDocument.ParseValue(ref reader).RootElement.Clone();
|
||||
T tObject = new T();
|
||||
|
||||
foreach (JsonProperty jKey in jElement.EnumerateObject())
|
||||
foreach (JsonProperty jProperty in jElement.EnumerateObject())
|
||||
{
|
||||
InnerTypedJsonProperty? typedJsonProperty = typedJsonProperties.SingleOrDefault(e => e.PropertyName == jKey.Name);
|
||||
if (typedJsonProperty != null)
|
||||
InnerTypedJsonPropertyInfo? typedJsonPropertyInfo = typedJsonProperties.SingleOrDefault(e => e.PropertyName == jProperty.Name);
|
||||
if (typedJsonPropertyInfo != null)
|
||||
{
|
||||
// 处理普通属性
|
||||
JsonSerializerOptions tmpOptions = options;
|
||||
if (typedJsonProperty.JsonConverter != null)
|
||||
JsonSerializerOptions tmpOptions = GetClonedJsonSerializerOptions(options, typedJsonPropertyInfo.JsonConverter);
|
||||
object? propertyValue = jProperty.Value.Deserialize(typedJsonPropertyInfo.PropertyType, tmpOptions);
|
||||
typedJsonPropertyInfo.PropertyInfo.SetValue(tObject, propertyValue);
|
||||
}
|
||||
else if (TryMatchNArrayIndex(jProperty.Name, out int index))
|
||||
{
|
||||
tmpOptions = new JsonSerializerOptions(options);
|
||||
tmpOptions.Converters.Add(typedJsonProperty.JsonConverter);
|
||||
typedJsonPropertyInfo = typedJsonProperties.Single(e => e.PropertyIsNArray);
|
||||
|
||||
Array? propertyValue = typedJsonPropertyInfo.PropertyInfo.GetValue(tObject) as Array;
|
||||
ReflectionUtility.CreateOrExpandArray(ref propertyValue, typedJsonPropertyInfo.PropertyType.GetElementType()!, index + 1);
|
||||
ReflectionUtility.CreateOrExpandArrayElement(propertyValue!, index, (object element) =>
|
||||
{
|
||||
InnerTypedJsonPropertyInfo? insider = GetTypedJsonProperties(element.GetType())
|
||||
.SingleOrDefault(p => string.Equals(p.PropertyName.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, index.ToString()), jProperty.Name));
|
||||
if (insider != null)
|
||||
{
|
||||
JsonSerializerOptions tmpOptions = GetClonedJsonSerializerOptions(options, insider.JsonConverter);
|
||||
object? elementPropertyValue = JsonSerializer.Deserialize(jProperty.Value, insider.PropertyType, tmpOptions)!;
|
||||
insider.PropertyInfo.SetValue(element, elementPropertyValue);
|
||||
}
|
||||
|
||||
object? value = JsonSerializer.Deserialize(jKey.Value, typedJsonProperty.PropertyType, tmpOptions);
|
||||
typedJsonProperty.PropertyInfo.SetValue(tObject, value);
|
||||
}
|
||||
else if (TryMatchNArrayIndex(jKey.Name, out int index))
|
||||
{
|
||||
// 处理 $n 属性
|
||||
InnerTypedJsonProperty narrayJsonProperty = typedJsonProperties.Single(e => e.PropertyIsNArray);
|
||||
object? value = narrayJsonProperty.PropertyInfo.GetValue(tObject);
|
||||
return element;
|
||||
});
|
||||
|
||||
JsonSerializerOptions tmpOptions = options;
|
||||
|
||||
Array array = CreateOrExpandNArray(value, narrayJsonProperty.PropertyType.GetElementType()!, index + 1);
|
||||
object? element = CreateOrUpdateNArrayElement(array, index, jKey.Name, jKey.Value, tmpOptions);
|
||||
narrayJsonProperty.PropertyInfo.SetValue(tObject, array);
|
||||
typedJsonPropertyInfo.PropertyInfo.SetValue(tObject, propertyValue);
|
||||
}
|
||||
}
|
||||
return tObject;
|
||||
@@ -103,48 +108,39 @@ namespace System.Text.Json.Converters
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (InnerTypedJsonProperty typedJsonProperty in GetTypedJsonProperties(value.GetType()))
|
||||
foreach (InnerTypedJsonPropertyInfo typedJsonProperty in GetTypedJsonProperties(value.GetType()))
|
||||
{
|
||||
if (!typedJsonProperty.PropertyIsNArray)
|
||||
{
|
||||
// 处理普通属性
|
||||
string propertyKey = typedJsonProperty.PropertyName;
|
||||
string propertyName = typedJsonProperty.PropertyName;
|
||||
object? propertyValue = typedJsonProperty.PropertyInfo.GetValue(value);
|
||||
if (propertyValue is null)
|
||||
{
|
||||
if (options.DefaultIgnoreCondition == JsonIgnoreCondition.Always || options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingNull)
|
||||
continue;
|
||||
}
|
||||
else if (propertyValue == (propertyValue.GetType().IsValueType ? Activator.CreateInstance(propertyValue.GetType()) : null))
|
||||
if (propertyValue == (typedJsonProperty.PropertyType.IsValueType ? Activator.CreateInstance(typedJsonProperty.PropertyType) : null))
|
||||
{
|
||||
if (options.DefaultIgnoreCondition == JsonIgnoreCondition.Always || options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingDefault)
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonSerializerOptions tmpOptions = options;
|
||||
JsonConverter? tmpConverter = typedJsonProperty.JsonConverter;
|
||||
if (tmpConverter != null)
|
||||
{
|
||||
tmpOptions = new JsonSerializerOptions(options);
|
||||
tmpOptions.Converters.Add(tmpConverter);
|
||||
}
|
||||
|
||||
if (tmpConverter != null && CheckTypeIsSubclassOf(tmpConverter.GetType(), typeof(TextualObjectInJsonFormatConverterBase<>)))
|
||||
JsonSerializerOptions tmpOptions = GetClonedJsonSerializerOptions(options, typedJsonProperty.JsonConverter);
|
||||
if (typedJsonProperty.JsonConverter != null && ReflectionUtility.CheckTypeIsSubclassOf(typedJsonProperty.JsonConverter.GetType(), typeof(TextualObjectInJsonFormatConverterBase<>)))
|
||||
{
|
||||
// TODO: 优化
|
||||
tmpOptions.Converters.Remove(tmpConverter);
|
||||
writer.WritePropertyName(propertyKey);
|
||||
tmpOptions.Converters.Remove(typedJsonProperty.JsonConverter);
|
||||
writer.WritePropertyName(propertyName);
|
||||
writer.WriteStringValue(JsonSerializer.Serialize(propertyValue, tmpOptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName(propertyKey);
|
||||
writer.WritePropertyName(propertyName);
|
||||
writer.WriteRawValue(JsonSerializer.Serialize(propertyValue, tmpOptions), skipInputValidation: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 处理 $n 属性
|
||||
Array? array = (Array?)typedJsonProperty.PropertyInfo.GetValue(value);
|
||||
if (array is null)
|
||||
continue;
|
||||
@@ -155,19 +151,12 @@ namespace System.Text.Json.Converters
|
||||
if (element is null)
|
||||
continue;
|
||||
|
||||
JsonSerializerOptions tmpOptions = options;
|
||||
JsonConverter? tmpConverter = GetTypedJsonConverter(element.GetType());
|
||||
if (tmpConverter != null)
|
||||
{
|
||||
tmpOptions = new JsonSerializerOptions(options);
|
||||
tmpOptions.Converters.Add(tmpConverter);
|
||||
}
|
||||
|
||||
JsonSerializerOptions tmpOptions = GetClonedJsonSerializerOptions(options, GetTypedJsonConverter(element.GetType()));
|
||||
JsonObject jSubObject = JsonSerializer.SerializeToNode(element, tmpOptions)!.AsObject();
|
||||
foreach (KeyValuePair<string, JsonNode?> jSubKey in jSubObject)
|
||||
foreach (KeyValuePair<string, JsonNode?> jSubProperty in jSubObject)
|
||||
{
|
||||
writer.WritePropertyName(jSubKey.Key.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, i.ToString()));
|
||||
writer.WriteRawValue(jSubKey.Value?.ToJsonString(tmpOptions)!, skipInputValidation: true);
|
||||
writer.WritePropertyName(jSubProperty.Key.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, i.ToString()));
|
||||
writer.WriteRawValue(jSubProperty.Value?.ToJsonString(tmpOptions)!, skipInputValidation: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,6 +165,44 @@ namespace System.Text.Json.Converters
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
private static InnerTypedJsonPropertyInfo[] GetTypedJsonProperties(Type type)
|
||||
{
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
string mappedKey = type.AssemblyQualifiedName ?? type.GetHashCode().ToString();
|
||||
InnerTypedJsonPropertyInfo[]? mappedValue = (InnerTypedJsonPropertyInfo[]?)_mappedTypeJsonProperties[mappedKey];
|
||||
|
||||
if (mappedValue == null)
|
||||
{
|
||||
mappedValue = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(p =>
|
||||
{
|
||||
if (p.CanWrite || p.GetCustomAttributes<JsonIncludeAttribute>(inherit: true).Any())
|
||||
{
|
||||
return !p.GetCustomAttributes<JsonIgnoreAttribute>(inherit: false).Any();
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.Select(p =>
|
||||
{
|
||||
string name = p.GetCustomAttribute<JsonPropertyNameAttribute>(inherit: true)?.Name ?? p.Name;
|
||||
return new InnerTypedJsonPropertyInfo
|
||||
(
|
||||
propertyName: name,
|
||||
propertyInfo: p,
|
||||
propertyIsNArray: PROPERTY_NAME_NARRAY.Equals(name) && p.PropertyType.IsArray && p.PropertyType.GetElementType()!.IsClass,
|
||||
jsonConverter: GetTypedJsonConverter(p)
|
||||
);
|
||||
})
|
||||
.OrderBy(e => e.PropertyInfo.GetCustomAttribute<JsonPropertyOrderAttribute>(inherit: true)?.Order)
|
||||
.ToArray();
|
||||
_mappedTypeJsonProperties[mappedKey] = mappedValue;
|
||||
}
|
||||
|
||||
return mappedValue;
|
||||
}
|
||||
|
||||
private static JsonConverter? GetTypedJsonConverter(MemberInfo? memberInfo)
|
||||
{
|
||||
if (memberInfo == null)
|
||||
@@ -206,67 +233,17 @@ namespace System.Text.Json.Converters
|
||||
.FirstOrDefault(converter => converter != null);
|
||||
}
|
||||
|
||||
private static InnerTypedJsonProperty[] GetTypedJsonProperties(Type type)
|
||||
private static JsonSerializerOptions GetClonedJsonSerializerOptions(JsonSerializerOptions options, JsonConverter? converter)
|
||||
{
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
JsonSerializerOptions optionsCopy = options;
|
||||
|
||||
string mappedTypeKey = type.AssemblyQualifiedName ?? type.GetHashCode().ToString();
|
||||
InnerTypedJsonProperty[]? typedJsonProperties = (InnerTypedJsonProperty[]?)_mappedTypeJsonProperties[mappedTypeKey];
|
||||
|
||||
if (typedJsonProperties == null)
|
||||
if (converter != null)
|
||||
{
|
||||
typedJsonProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(p =>
|
||||
{
|
||||
if (p.CanWrite || p.GetCustomAttributes<JsonIncludeAttribute>(inherit: true).Any())
|
||||
{
|
||||
return !p.GetCustomAttributes<JsonIgnoreAttribute>(inherit: false).Any();
|
||||
optionsCopy = new JsonSerializerOptions(options);
|
||||
optionsCopy.Converters.Add(converter);
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.Select(p =>
|
||||
{
|
||||
string name = p.GetCustomAttribute<JsonPropertyNameAttribute>(inherit: true)?.Name ?? p.Name;
|
||||
if (name.Equals("modify_time_$n"))
|
||||
{
|
||||
var cs = p.GetCustomAttributes<JsonConverterAttribute>(inherit: true);
|
||||
}
|
||||
|
||||
return new InnerTypedJsonProperty
|
||||
(
|
||||
propertyName: name,
|
||||
propertyInfo: p,
|
||||
propertyIsNArray: PROPERTY_NAME_NARRAY.Equals(name) && p.PropertyType.IsArray && p.PropertyType.GetElementType()!.IsClass,
|
||||
jsonConverter: GetTypedJsonConverter(p)
|
||||
);
|
||||
})
|
||||
.OrderBy(e => e.PropertyInfo.GetCustomAttribute<JsonPropertyOrderAttribute>(inherit: true)?.Order)
|
||||
.ToArray();
|
||||
_mappedTypeJsonProperties[mappedTypeKey] = typedJsonProperties;
|
||||
}
|
||||
|
||||
return typedJsonProperties;
|
||||
}
|
||||
|
||||
private static bool CheckTypeIsSubclassOf(Type type, Type generic)
|
||||
{
|
||||
bool IsTheRawGenericType(Type test)
|
||||
=> generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
|
||||
|
||||
bool isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
|
||||
if (isTheRawGenericType)
|
||||
return true;
|
||||
|
||||
Type? tmp = type;
|
||||
while (tmp != null && tmp != typeof(object))
|
||||
{
|
||||
isTheRawGenericType = IsTheRawGenericType(tmp);
|
||||
if (isTheRawGenericType) return true;
|
||||
tmp = tmp.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
return optionsCopy;
|
||||
}
|
||||
|
||||
private static bool TryMatchNArrayIndex(string key, out int index)
|
||||
@@ -282,70 +259,5 @@ namespace System.Text.Json.Converters
|
||||
index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Array CreateOrExpandNArray(object? array, Type elementType, int capacity)
|
||||
{
|
||||
if (elementType == null) throw new ArgumentNullException(nameof(elementType));
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
|
||||
if (array == null)
|
||||
{
|
||||
return Array.CreateInstance(elementType, capacity);
|
||||
}
|
||||
|
||||
Array src = (Array)array;
|
||||
if (src.Length < capacity)
|
||||
{
|
||||
Array dst = Array.CreateInstance(elementType, capacity);
|
||||
Array.Copy(src, dst, src.Length);
|
||||
return dst;
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
private static object CreateOrUpdateNArrayElement(Array array, int index, string jKey, JsonElement jValue, JsonSerializerOptions serializerOptions)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
object? element = array.GetValue(index);
|
||||
Type elementType = array.GetType().GetElementType()!;
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
|
||||
if (elementType.IsAbstract || elementType.IsInterface)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
else if (elementType.IsArray)
|
||||
{
|
||||
element = Array.CreateInstance(elementType, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
element = Activator.CreateInstance(elementType);
|
||||
}
|
||||
|
||||
array.SetValue(element, index);
|
||||
}
|
||||
|
||||
InnerTypedJsonProperty? typedJsonProperty = GetTypedJsonProperties(elementType)
|
||||
.SingleOrDefault(p => string.Equals(p.PropertyName.Replace(PROPERTY_WILDCARD_NARRAY_ELEMENT, index.ToString()), jKey));
|
||||
if (typedJsonProperty != null)
|
||||
{
|
||||
if (typedJsonProperty.JsonConverter != null)
|
||||
{
|
||||
serializerOptions = new JsonSerializerOptions(serializerOptions);
|
||||
serializerOptions.Converters.Add(typedJsonProperty.JsonConverter);
|
||||
}
|
||||
|
||||
object? obj = JsonSerializer.Deserialize(jValue, typedJsonProperty.PropertyType, serializerOptions)!;
|
||||
typedJsonProperty.PropertyInfo.SetValue(element, obj);
|
||||
}
|
||||
|
||||
return element!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
||||
{
|
||||
internal static class ReflectionUtility
|
||||
{
|
||||
public static void CreateOrExpandArray(ref Array? array, Type elementType, int capacity)
|
||||
{
|
||||
if (elementType == null) throw new ArgumentNullException(nameof(elementType));
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
|
||||
if (array == null)
|
||||
{
|
||||
array = Array.CreateInstance(elementType, capacity);
|
||||
}
|
||||
else if (array.Length < capacity)
|
||||
{
|
||||
Array tmpArray = Array.CreateInstance(elementType, capacity);
|
||||
Array.Copy(array, tmpArray, array.Length);
|
||||
array = tmpArray;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateOrExpandArrayElement(Array array, int index, Func<object, object>? updateFactory)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||
if (index < 0 || index >= array.Length) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (updateFactory == null) throw new ArgumentNullException(nameof(updateFactory));
|
||||
|
||||
object? element = array.GetValue(index);
|
||||
if (element is null)
|
||||
{
|
||||
Type elementType = array.GetType().GetElementType()!;
|
||||
|
||||
if (elementType.IsAbstract || elementType.IsInterface)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
else if (elementType.IsArray)
|
||||
{
|
||||
element = Array.CreateInstance(elementType, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
element = Activator.CreateInstance(elementType);
|
||||
}
|
||||
}
|
||||
|
||||
element = updateFactory(element);
|
||||
array.SetValue(element, index);
|
||||
}
|
||||
|
||||
public static bool CheckTypeIsSubclassOf(Type childType, Type baseType)
|
||||
{
|
||||
bool IsTheRawGenericType(Type type)
|
||||
=> baseType == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
|
||||
|
||||
bool isTheRawGenericType = childType.GetInterfaces().Any(IsTheRawGenericType);
|
||||
if (isTheRawGenericType)
|
||||
return true;
|
||||
|
||||
Type? t = childType;
|
||||
while (t != null && t != typeof(object))
|
||||
{
|
||||
isTheRawGenericType = IsTheRawGenericType(t);
|
||||
if (isTheRawGenericType) return true;
|
||||
t = t.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,6 +10,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove=".gitignore" />
|
||||
<None Remove="appsettings - 复制.json" />
|
||||
<None Remove="appsettings.local.json" />
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"TestConfig": {
|
||||
"AppId": "请在此填写用于测试的微信 AppId",
|
||||
"MerchantId": "请在此填写用于测试的微信商户号",
|
||||
"MerchantSecret": "请在此填写用于测试的微信商户 API 密钥",
|
||||
"MerchantCertificateBase64String": "请在此填写用于测试的微信商户证书内容的 Base64 字节数组",
|
||||
"AppId": "请在此填写用于测试的微信 AppId",
|
||||
"OpenId": "请在此填写用于测试的微信用户唯一标识"
|
||||
},
|
||||
"ProjectSourceDirectory": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.TenpayV2\\",
|
||||
|
Reference in New Issue
Block a user