AyCode.Core/AyCode.Core/Extensions/SerializeObjectExtensions.cs

728 lines
28 KiB
C#

using System.Buffers;
using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using AyCode.Core.Interfaces;
using AyCode.Core.Serializers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Serializers.Jsons;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using static AyCode.Core.Helpers.JsonUtilities;
using static AyCode.Core.Serializers.Binaries.AcBinaryDeserializer;
namespace AyCode.Core.Extensions;
/// <summary>
/// High-performance Base62 encoder for compact $id/$ref values.
/// </summary>
internal static class Base62
{
private const string Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Encode(long value)
{
if (value == 0) return "0";
var isNegative = value < 0;
if (isNegative) value = -value;
Span<char> buffer = stackalloc char[16];
var index = buffer.Length;
while (value > 0)
{
buffer[--index] = Alphabet[(int)(value % 62)];
value /= 62;
}
if (isNegative) buffer[--index] = '-';
return new string(buffer[index..]);
}
}
/// <summary>
/// High-performance hybrid reference resolver using Base62 encoded semantic IDs.
/// </summary>
public class HybridReferenceResolver : IReferenceResolver
{
internal Dictionary<string, object>? _idToObject;
internal Dictionary<object, string>? _objectToId;
internal HashSet<string>? _referencedIds;
private int _nextNumericId = 1;
private static readonly ConcurrentDictionary<Type, Func<object, object?>> IdGetterCache = new();
public bool IsForMerge { get; }
private readonly int _estimatedObjectCount;
public HybridReferenceResolver(bool isForMerge = false, int estimatedObjectCount = 64)
{
IsForMerge = isForMerge;
_estimatedObjectCount = estimatedObjectCount;
}
internal HashSet<string> ReferencedIds => _referencedIds ??=
new HashSet<string>(_estimatedObjectCount / 4, StringComparer.Ordinal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Dictionary<string, object> GetIdToObject() =>
_idToObject ??= new Dictionary<string, object>(_estimatedObjectCount, StringComparer.Ordinal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Dictionary<object, string> GetObjectToId() =>
_objectToId ??= new Dictionary<object, string>(_estimatedObjectCount, AyCode.Core.Serializers.ReferenceEqualityComparer.Instance);
public void AddReference(object context, string reference, object value)
{
GetIdToObject()[reference] = value;
GetObjectToId()[value] = reference;
}
public string GetReference(object context, object value)
{
var objectToId = GetObjectToId();
if (objectToId.TryGetValue(value, out var existingId))
{
if (!IsForMerge) ReferencedIds.Add(existingId);
return existingId;
}
var type = value.GetType();
var (isId, idType) = GetIdInfo(type);
string newRef;
if (isId && idType != null)
{
var idGetter = GetOrCreateIdGetter(type);
var idValue = idGetter(value);
if (idValue != null && !IsDefaultValue(idValue, idType))
{
var typeId = TypeCache.GetTypeId(type);
var objectIdAsLong = TypeCache.IdToLong(idValue);
var semanticId = TypeCache.CreateSemanticId(typeId, objectIdAsLong);
newRef = Base62.Encode(semanticId);
}
else
newRef = Base62.Encode(-_nextNumericId++);
}
else
newRef = Base62.Encode(-_nextNumericId++);
GetIdToObject()[newRef] = value;
objectToId[value] = newRef;
return newRef;
}
public bool IsReferenced(object context, object value) => _objectToId?.ContainsKey(value) ?? false;
public object ResolveReference(object context, string reference) =>
_idToObject != null && _idToObject.TryGetValue(reference, out var value) ? value : null!;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Func<object, object?> GetOrCreateIdGetter(Type type) =>
IdGetterCache.GetOrAdd(type, static t =>
{
var prop = t.GetProperty("Id");
if (prop == null) return static _ => null;
var getMethod = prop.GetGetMethod();
if (getMethod == null) return static _ => null;
return obj => getMethod.Invoke(obj, null);
});
}
internal static class JsonReferencePostProcessor
{
private const string IdMarker = "\"$id\"";
public static string RemoveUnreferencedIds(string json, HashSet<string>? referencedIds)
{
if (!json.Contains(IdMarker)) return json;
return referencedIds == null || referencedIds.Count == 0
? RemoveAllIdsSpan(json)
: RemoveUnreferencedIdsSpan(json, referencedIds);
}
private static string RemoveAllIdsSpan(string json)
{
var sb = new StringBuilder(json.Length);
var lastCopyEnd = 0;
var searchStart = 0;
while (searchStart < json.Length)
{
var idIndex = json.IndexOf(IdMarker, searchStart, StringComparison.Ordinal);
if (idIndex < 0) break;
if (idIndex > lastCopyEnd) sb.Append(json, lastCopyEnd, idIndex - lastCopyEnd);
var endIndex = SkipIdEntry(json, idIndex);
lastCopyEnd = endIndex;
searchStart = endIndex;
}
if (lastCopyEnd < json.Length) sb.Append(json, lastCopyEnd, json.Length - lastCopyEnd);
return sb.Length == json.Length ? json : sb.ToString();
}
private static string RemoveUnreferencedIdsSpan(string json, HashSet<string> referencedIds)
{
var sb = new StringBuilder(json.Length);
var lastCopyEnd = 0;
var searchStart = 0;
while (searchStart < json.Length)
{
var idIndex = json.IndexOf(IdMarker, searchStart, StringComparison.Ordinal);
if (idIndex < 0) break;
var valueStart = idIndex + IdMarker.Length;
while (valueStart < json.Length && (json[valueStart] == ' ' || json[valueStart] == ':'))
valueStart++;
string? idValue = null;
var valueEnd = valueStart;
if (valueStart < json.Length && json[valueStart] == '"')
{
valueStart++;
valueEnd = valueStart;
while (valueEnd < json.Length && json[valueEnd] != '"') valueEnd++;
idValue = json.Substring(valueStart, valueEnd - valueStart);
valueEnd++;
}
while (valueEnd < json.Length && (json[valueEnd] == ' ' || json[valueEnd] == ','))
valueEnd++;
if (idValue != null && referencedIds.Contains(idValue))
searchStart = valueEnd;
else
{
if (idIndex > lastCopyEnd) sb.Append(json, lastCopyEnd, idIndex - lastCopyEnd);
lastCopyEnd = valueEnd;
searchStart = valueEnd;
}
}
if (lastCopyEnd == 0) return json;
if (lastCopyEnd < json.Length) sb.Append(json, lastCopyEnd, json.Length - lastCopyEnd);
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int SkipIdEntry(string json, int idIndex)
{
var pos = idIndex + IdMarker.Length;
while (pos < json.Length && (json[pos] == ' ' || json[pos] == ':')) pos++;
if (pos < json.Length && json[pos] == '"')
{
pos++;
while (pos < json.Length && json[pos] != '"') pos++;
if (pos < json.Length) pos++;
}
while (pos < json.Length && (json[pos] == ' ' || json[pos] == ',')) pos++;
return pos;
}
public static HashSet<string> CollectReferencedIds(string json)
{
const string refMarker = "\"$ref\"";
var result = new HashSet<string>(StringComparer.Ordinal);
var searchStart = 0;
while (searchStart < json.Length)
{
var refIndex = json.IndexOf(refMarker, searchStart, StringComparison.Ordinal);
if (refIndex < 0) break;
var valueStart = refIndex + refMarker.Length;
while (valueStart < json.Length && (json[valueStart] == ' ' || json[valueStart] == ':'))
valueStart++;
if (valueStart < json.Length && json[valueStart] == '"')
{
valueStart++;
var valueEnd = valueStart;
while (valueEnd < json.Length && json[valueEnd] != '"') valueEnd++;
if (valueEnd > valueStart) result.Add(json.Substring(valueStart, valueEnd - valueStart));
searchStart = valueEnd + 1;
}
else
searchStart = valueStart;
}
return result;
}
}
internal sealed class PooledStringWriter : StringWriter
{
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy { InitialCapacity = 4096, MaximumRetainedCapacity = 4 * 1024 * 1024 });
private readonly StringBuilder _pooledBuilder;
private bool _disposed;
private PooledStringWriter(StringBuilder sb) : base(sb) => _pooledBuilder = sb;
public static PooledStringWriter Rent()
{
var sb = StringBuilderPool.Get();
sb.Clear();
return new PooledStringWriter(sb);
}
protected override void Dispose(bool disposing)
{
if (!_disposed) { _disposed = true; StringBuilderPool.Return(_pooledBuilder); }
base.Dispose(disposing);
}
}
internal interface ObjectPool<T> where T : class { T Get(); void Return(T obj); }
internal sealed class DefaultObjectPool<T> : ObjectPool<T> where T : class
{
[ThreadStatic] private static T? _threadLocalItem;
private readonly ConcurrentQueue<T> _pool = new();
private readonly IPooledObjectPolicy<T> _policy;
private const int MaxPoolSize = 8;
public DefaultObjectPool(IPooledObjectPolicy<T> policy) => _policy = policy;
public T Get()
{
var item = _threadLocalItem;
if (item != null) { _threadLocalItem = null; return item; }
return _pool.TryDequeue(out item) ? item : _policy.Create();
}
public void Return(T obj)
{
if (!_policy.Return(obj)) return;
if (_threadLocalItem == null) { _threadLocalItem = obj; return; }
if (_pool.Count < MaxPoolSize) _pool.Enqueue(obj);
}
}
internal interface IPooledObjectPolicy<T> { T Create(); bool Return(T obj); }
internal sealed class StringBuilderPooledObjectPolicy : IPooledObjectPolicy<StringBuilder>
{
public int InitialCapacity { get; init; } = 256;
public int MaximumRetainedCapacity { get; init; } = 4 * 1024 * 1024;
public StringBuilder Create() => new(InitialCapacity);
public bool Return(StringBuilder obj) { if (obj.Capacity > MaximumRetainedCapacity) return false; obj.Clear(); return true; }
}
public static class SerializeObjectExtensions
{
private static readonly UnifiedMergeContractResolver SharedContractResolver = new();
private static readonly Dictionary<object, object> EmptyContextDict = new();
public static JsonSerializerSettings Options => new()
{
ContractResolver = SharedContractResolver,
Context = new StreamingContext(StreamingContextStates.All, EmptyContextDict),
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ReferenceResolverProvider = () => new HybridReferenceResolver(),
NullValueHandling = NullValueHandling.Ignore,
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead,
Formatting = Formatting.None,
};
#region JSON Serialization
/// <summary>
/// Serialize object to JSON string with default options.
/// </summary>
public static string ToJson<T>(this T source) => AcJsonSerializer.Serialize(source);
/// <summary>
/// Serialize object to JSON string with specified options.
/// </summary>
public static string ToJson<T>(this T source, AcJsonSerializerOptions options)
=> AcJsonSerializer.Serialize(source, options);
public static string ToJson<T>(this IQueryable<T> source) where T : class, IAcSerializableToJson
=> AcJsonSerializer.Serialize(source);
public static string ToJson<T>(this IQueryable<T> source, AcJsonSerializerOptions options) where T : class, IAcSerializableToJson
=> AcJsonSerializer.Serialize(source, options);
public static string ToJson<T>(this IEnumerable<T> source) where T : class, IAcSerializableToJson
=> AcJsonSerializer.Serialize(source);
public static string ToJson<T>(this IEnumerable<T> source, AcJsonSerializerOptions options) where T : class, IAcSerializableToJson
=> AcJsonSerializer.Serialize(source, options);
/// <summary>
/// Deserialize JSON to object with default options.
/// </summary>
public static T? JsonTo<T>(this string json)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.Deserialize<T>(json);
}
/// <summary>
/// Deserialize JSON to object with specified options.
/// </summary>
public static T? JsonTo<T>(this string json, AcJsonSerializerOptions options)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.Deserialize<T>(json, options);
}
/// <summary>
/// Deserialize UTF-8 encoded JSON bytes to object with default options.
/// Zero-allocation path - no string conversion needed.
/// </summary>
public static T? JsonTo<T>(this ReadOnlySpan<byte> utf8Json)
=> AcJsonDeserializer.Deserialize<T>(utf8Json);
/// <summary>
/// Deserialize UTF-8 encoded JSON bytes to object with specified options.
/// Zero-allocation path - no string conversion needed.
/// </summary>
public static T? JsonTo<T>(this ReadOnlySpan<byte> utf8Json, AcJsonSerializerOptions options)
=> AcJsonDeserializer.Deserialize<T>(utf8Json, options);
/// <summary>
/// Deserialize UTF-8 encoded JSON bytes to object with default options.
/// Zero-allocation path - no string conversion needed.
/// </summary>
public static T? JsonTo<T>(this byte[] utf8Json)
=> AcJsonDeserializer.Deserialize<T>(utf8Json.AsSpan());
/// <summary>
/// Deserialize UTF-8 encoded JSON bytes to object with specified options.
/// Zero-allocation path - no string conversion needed.
/// </summary>
public static T? JsonTo<T>(this byte[] utf8Json, AcJsonSerializerOptions options)
=> AcJsonDeserializer.Deserialize<T>(utf8Json.AsSpan(), options);
/// <summary>
/// Deserialize JSON to specified type with default options.
/// </summary>
public static object? JsonTo(this string json, Type toType)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.Deserialize(json, toType);
}
/// <summary>
/// Deserialize JSON to specified type with specified options.
/// </summary>
public static object? JsonTo(this string json, Type toType, AcJsonSerializerOptions options)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.Deserialize(json, toType, options);
}
/// <summary>
/// Populate existing object from JSON with default options.
/// </summary>
public static void JsonTo(this string json, object target)
{
json = UnwrapJsonString(json);
AcJsonDeserializer.Populate(json, target);
}
/// <summary>
/// Populate existing object from JSON with specified options.
/// </summary>
public static void JsonTo(this string json, object target, AcJsonSerializerOptions options)
{
json = UnwrapJsonString(json);
AcJsonDeserializer.Populate(json, target, options);
}
/// <summary>
/// Create a deserialize chain that parses JSON once and allows multiple deserializations.
/// Efficient for deserializing the same JSON to multiple different types.
/// Use with 'using' statement or call Dispose() when done.
/// </summary>
public static IDeserializeChain<T> JsonToChain<T>(this string json)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.CreateDeserializeChain<T>(json);
}
/// <summary>
/// Create a deserialize chain with options.
/// </summary>
public static IDeserializeChain<T> JsonToChain<T>(this string json, AcJsonSerializerOptions options)
{
json = UnwrapJsonString(json);
return AcJsonDeserializer.CreateDeserializeChain<T>(json, options);
}
/// <summary>
/// Create a populate chain that parses JSON once and allows populating multiple objects.
/// Efficient for populating multiple objects from the same JSON source.
/// Use with 'using' statement or call Dispose() when done.
/// </summary>
public static IDeserializeChain<object> JsonToChain(this string json, object target)
{
json = UnwrapJsonString(json);
var chain = AcJsonDeserializer.CreateDeserializeChain<object>(json);
chain.ThenPopulate(target);
return chain;
}
/// <summary>
/// Create a populate chain with options.
/// </summary>
public static IDeserializeChain<object> JsonToChain(this string json, object target, AcJsonSerializerOptions options)
{
json = UnwrapJsonString(json);
var chain = AcJsonDeserializer.CreateDeserializeChain<object>(json, options);
chain.ThenPopulate(target);
return chain;
}
#endregion
#region Any (JSON or Binary based on options)
public static object ToAny<T>(this T source, AcSerializerOptions options)
{
if (options.SerializerType == AcSerializerType.Json) return ToJson(source, (AcJsonSerializerOptions)options);
return ToBinary(source, (AcBinarySerializerOptions)options);
}
/// <summary>
/// Deserialize data (JSON string or binary byte[]) to object based on options.
/// </summary>
public static T? AnyTo<T>(this object data, AcSerializerOptions options)
{
if (options.SerializerType == AcSerializerType.Json)
return ((string)data).JsonTo<T>((AcJsonSerializerOptions)options);
return ((byte[])data).BinaryTo<T>();
}
/// <summary>
/// Deserialize data to specified type based on options.
/// </summary>
public static object? AnyTo(this object data, Type targetType, AcSerializerOptions options)
{
if (options.SerializerType == AcSerializerType.Json)
return ((string)data).JsonTo(targetType, (AcJsonSerializerOptions)options);
return ((byte[])data).BinaryTo(targetType);
}
/// <summary>
/// Populate existing object from data based on options.
/// </summary>
public static void AnyTo<T>(this object data, T target, AcSerializerOptions options) where T : class
{
if (options.SerializerType == AcSerializerType.Json)
((string)data).JsonTo(target, (AcJsonSerializerOptions)options);
else
((byte[])data).BinaryTo(target);
}
/// <summary>
/// Populate existing object with merge semantics based on options.
/// </summary>
public static void AnyToMerge<T>(this object data, T target, AcSerializerOptions options) where T : class
{
if (options.SerializerType == AcSerializerType.Json)
((string)data).JsonTo(target, (AcJsonSerializerOptions)options);
else
((byte[])data).BinaryToMerge(target);
}
#endregion
#region Binary Serialization
/// <summary>
/// Serialize object to binary byte array with default options.
/// </summary>
public static byte[] ToBinary<T>(this T source) => AcBinarySerializer.Serialize(source);
/// <summary>
/// Serialize object to binary byte array with specified options.
/// </summary>
public static byte[] ToBinary<T>(this T source, AcBinarySerializerOptions options)
=> AcBinarySerializer.Serialize(source, options);
/// <summary>
/// Serialize object directly to an IBufferWriter for zero-copy scenarios.
/// </summary>
public static void ToBinary<T>(this T source, IBufferWriter<byte> writer)
=> AcBinarySerializer.Serialize(source, writer, AcBinarySerializerOptions.Default);
/// <summary>
/// Serialize object directly to an IBufferWriter with specified options.
/// </summary>
public static void ToBinary<T>(this T source, IBufferWriter<byte> writer, AcBinarySerializerOptions options)
=> AcBinarySerializer.Serialize(source, writer, options);
/// <summary>
/// Get the serialized binary size without allocating the final array.
/// </summary>
public static int GetBinarySize<T>(this T source)
=> AcBinarySerializer.GetSerializedSize(source, AcBinarySerializerOptions.Default);
/// <summary>
/// Get the serialized binary size with specified options.
/// </summary>
public static int GetBinarySize<T>(this T source, AcBinarySerializerOptions options)
=> AcBinarySerializer.GetSerializedSize(source, options);
/// <summary>
/// Deserialize binary data to object.
/// </summary>
public static T? BinaryTo<T>(this byte[] data)
=> AcBinaryDeserializer.Deserialize<T>(data);
/// <summary>
/// Deserialize binary data to specified type.
/// </summary>
public static object? BinaryTo(this byte[] data, Type targetType)
=> AcBinaryDeserializer.Deserialize(data, targetType);
/// <summary>
/// Populate existing object from binary data.
/// </summary>
public static void BinaryTo<T>(this byte[] data, T target) where T : class
=> AcBinaryDeserializer.Populate(data, target);
/// <summary>
/// Populate existing object from binary data with merge semantics for IId collections.
/// </summary>
public static void BinaryToMerge<T>(this byte[] data, T target) where T : class
=> AcBinaryDeserializer.PopulateMerge(data, target);
/// <summary>
/// Create a deserialize chain that parses binary data once and allows multiple deserializations.
/// Efficient for deserializing the same binary to multiple different types.
/// Use with 'using' statement or call Dispose() when done.
/// </summary>
public static IDeserializeChain<T> BinaryToChain<T>(this byte[] data)
=> AcBinaryDeserializer.CreateDeserializeChain<T>(data);
/// <summary>
/// Create a deserialize chain with options.
/// </summary>
public static IDeserializeChain<T> BinaryToChain<T>(this byte[] data, AcBinarySerializerOptions options)
=> AcBinaryDeserializer.CreateDeserializeChain<T>(data, options);
/// <summary>
/// Create a populate chain that parses binary data once and allows populating multiple objects.
/// Efficient for populating multiple objects from the same binary source.
/// Use with 'using' statement or call Dispose() when done.
/// </summary>
public static IDeserializeChain<T> BinaryToChain<T>(this byte[] data, T target) where T : class
{
var chain = AcBinaryDeserializer.CreateDeserializeChain<T>(data);
chain.ThenPopulate(target);
return chain;
}
/// <summary>
/// Create a populate chain with options.
/// </summary>
public static IDeserializeChain<T> BinaryToChain<T>(this byte[] data, T target, AcBinarySerializerOptions options) where T : class
{
var chain = AcBinaryDeserializer.CreateDeserializeChain<T>(data, options);
chain.ThenPopulate(target);
return chain;
}
#endregion
#region Clone and Copy (Binary-based, zero intermediate allocation)
/// <summary>
/// Clone object via binary serialization (zero intermediate byte[] allocation).
/// Uses ArrayBufferWriter to serialize directly into a buffer, then deserializes from the span.
/// </summary>
public static TDestination? CloneTo<TDestination>(this object? src) where TDestination : class
{
if (src == null) return null;
var buffer = new ArrayBufferWriter<byte>(256);
AcBinarySerializer.Serialize(src, buffer, AcBinarySerializerOptions.Default);
MemoryMarshal.TryGetArray<byte>(buffer.WrittenMemory, out var seg);
return AcBinaryDeserializer.Deserialize<TDestination>(seg.Array!, seg.Offset, seg.Count);
}
/// <summary>
/// Copy object properties to target via binary serialization (zero intermediate byte[] allocation).
/// Uses ArrayBufferWriter to serialize directly into a buffer, then populates target from the backing array.
/// </summary>
public static void CopyTo(this object? src, object target)
{
if (src == null) return;
var buffer = new ArrayBufferWriter<byte>(256);
AcBinarySerializer.Serialize(src, buffer, AcBinarySerializerOptions.Default);
MemoryMarshal.TryGetArray<byte>(buffer.WrittenMemory, out var seg);
AcBinaryDeserializer.Populate(seg.Array!, seg.Offset, seg.Count, target);
}
#endregion
}
public class IgnoreAndRenamePropertySerializerContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _ignores = new();
private readonly Dictionary<Type, HashSet<string>> _includes = new();
private readonly Dictionary<Type, Dictionary<string, string>> _renames = new();
public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
if (!_ignores.TryGetValue(type, out var set)) { set = new HashSet<string>(StringComparer.Ordinal); _ignores[type] = set; }
foreach (var prop in jsonPropertyNames) set.Add(prop);
}
public void IncludesProperty(Type type, params string[] jsonPropertyNames)
{
if (!_includes.TryGetValue(type, out var set)) { set = new HashSet<string>(StringComparer.Ordinal); _includes[type] = set; }
foreach (var prop in jsonPropertyNames) set.Add(prop);
}
public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
if (!_renames.TryGetValue(type, out var dict)) { dict = new Dictionary<string, string>(StringComparer.Ordinal); _renames[type] = dict; }
dict[propertyName] = newJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName) || !IsIncluded(property.DeclaringType, property.PropertyName))
{ property.ShouldSerialize = _ => false; property.Ignored = true; }
if (IsRenamed(property.DeclaringType, property.PropertyName, out var newName)) property.PropertyName = newName;
return property;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsIgnored(Type? type, string? name) => type != null && name != null && _ignores.TryGetValue(type, out var set) && set.Contains(name);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsIncluded(Type? type, string? name) => _includes.Count == 0 || (type != null && name != null && _includes.TryGetValue(type, out var set) && set.Contains(name));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsRenamed(Type? type, string? name, out string? newName)
{
if (type != null && name != null && _renames.TryGetValue(type, out var renames) && renames.TryGetValue(name, out newName)) return true;
newName = null; return false;
}
}