diff --git a/AyCode.Core.Server/AyCode.Core.Server.csproj b/AyCode.Core.Server/AyCode.Core.Server.csproj
index c3f7447..54de42a 100644
--- a/AyCode.Core.Server/AyCode.Core.Server.csproj
+++ b/AyCode.Core.Server/AyCode.Core.Server.csproj
@@ -7,6 +7,7 @@
+
diff --git a/AyCode.Core.Tests.Internal/AyCode.Core.Tests.Internal.csproj b/AyCode.Core.Tests.Internal/AyCode.Core.Tests.Internal.csproj
index 090fd43..70d0d20 100644
--- a/AyCode.Core.Tests.Internal/AyCode.Core.Tests.Internal.csproj
+++ b/AyCode.Core.Tests.Internal/AyCode.Core.Tests.Internal.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/AyCode.Core.Tests/AyCode.Core.Tests.csproj b/AyCode.Core.Tests/AyCode.Core.Tests.csproj
index d6378cc..ec7e61b 100644
--- a/AyCode.Core.Tests/AyCode.Core.Tests.csproj
+++ b/AyCode.Core.Tests/AyCode.Core.Tests.csproj
@@ -17,6 +17,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/AyCode.Core.Tests/JsonExtensionTests.cs b/AyCode.Core.Tests/JsonExtensionTests.cs
new file mode 100644
index 0000000..7f68a94
--- /dev/null
+++ b/AyCode.Core.Tests/JsonExtensionTests.cs
@@ -0,0 +1,1190 @@
+ο»Ώusing System.Runtime.Serialization;
+using AyCode.Core.Enums;
+using AyCode.Core.Extensions;
+using AyCode.Core.Interfaces;
+using AyCode.Core.Loggers;
+using Newtonsoft.Json;
+
+namespace AyCode.Core.Tests;
+
+[TestClass]
+public sealed class JsonExtensionTests
+{
+ [TestInitialize]
+ public void TestInit()
+ {
+ TestDataFactory.ResetIdCounter();
+ }
+
+ private static JsonSerializerSettings GetMergeSettings()
+ {
+ return SerializeObjectExtensions.Options;
+ //return new JsonSerializerSettings
+ //{
+ // ContractResolver = new UnifiedMergeContractResolver(),
+ // Context = new StreamingContext(StreamingContextStates.All, new Dictionary
diff --git a/AyCode.Core/Extensions/MergeContractResolver.cs b/AyCode.Core/Extensions/MergeContractResolver.cs
new file mode 100644
index 0000000..558f206
--- /dev/null
+++ b/AyCode.Core/Extensions/MergeContractResolver.cs
@@ -0,0 +1,991 @@
+ο»Ώusing System.Collections;
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using AyCode.Core.Interfaces;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace AyCode.Core.Extensions
+{
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+ public sealed class JsonNoMergeCollectionAttribute : Attribute { }
+
+ ///
+ /// Thread-safe object pool for reducing allocations
+ ///
+ internal sealed class ObjectPool where T : class, new()
+ {
+ private readonly ConcurrentBag _pool = new();
+ private readonly int _maxPoolSize;
+
+ public ObjectPool(int maxPoolSize = 32)
+ {
+ _maxPoolSize = maxPoolSize;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T Rent() => _pool.TryTake(out var item) ? item : new T();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Return(T item)
+ {
+ if (_pool.Count < _maxPoolSize)
+ {
+ _pool.Add(item);
+ }
+ }
+ }
+
+ ///
+ /// Cached property metadata for faster JSON processing
+ ///
+ internal sealed class CachedPropertyInfo
+ {
+ public PropertyInfo Property { get; }
+ public string Name { get; }
+ public Type PropertyType { get; }
+ public bool IsIId { get; }
+ public Type? IdType { get; }
+ public bool IsIIdCollection { get; }
+ public Type? CollectionElementType { get; }
+ public Type? CollectionElementIdType { get; }
+ public bool ShouldSkip { get; }
+ public bool CanRead { get; }
+ public bool HasIndexParameters { get; }
+
+ public CachedPropertyInfo(PropertyInfo prop)
+ {
+ Property = prop;
+ Name = prop.Name;
+ PropertyType = prop.PropertyType;
+ CanRead = prop.CanRead;
+ HasIndexParameters = prop.GetIndexParameters().Length > 0;
+
+ // Pre-compute skip condition
+ ShouldSkip = !CanRead || HasIndexParameters || TypeCache.HasJsonIgnoreAttribute(prop);
+
+ if (!ShouldSkip)
+ {
+ var (isId, idType) = TypeCache.GetIdInfo(PropertyType);
+ IsIId = isId;
+ IdType = idType;
+
+ if (!IsIId && typeof(IEnumerable).IsAssignableFrom(PropertyType) && PropertyType != typeof(string))
+ {
+ CollectionElementType = TypeCache.GetElementType(PropertyType);
+ if (CollectionElementType != null)
+ {
+ var (elemIsId, elemIdType) = TypeCache.GetIdInfo(CollectionElementType);
+ IsIIdCollection = elemIsId;
+ CollectionElementIdType = elemIdType;
+ }
+ }
+ }
+ }
+ }
+
+ static class TypeCache
+ {
+ // π OPTIMIZATION: Use ConcurrentDictionary for lock-free reads
+ private static readonly ConcurrentDictionary _idCache = new();
+ private static readonly ConcurrentDictionary _collectionElemCache = new();
+
+ // π OPTIMIZATION: Cache type names for semantic key generation
+ private static readonly ConcurrentDictionary _typeNameCache = new();
+
+ // π OPTIMIZATION: Cache fully processed property info for types
+ private static readonly ConcurrentDictionary _cachedPropertyInfoCache = new();
+
+ // π OPTIMIZATION: Cache JsonIgnore attribute check results per property
+ private static readonly ConcurrentDictionary _jsonIgnoreCache = new();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string GetTypeName(Type t)
+ {
+ return _typeNameCache.GetOrAdd(t, static type => type.Name);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static (bool IsId, Type? IdType) GetIdInfo(Type t)
+ {
+ return _idCache.GetOrAdd(t, static type =>
+ {
+ Type? foundInterface = null;
+ var interfaces = type.GetInterfaces();
+
+ for (var i = 0; i < interfaces.Length; i++)
+ {
+ var iface = interfaces[i];
+ if (!iface.IsGenericType || iface.GetGenericTypeDefinition() != typeof(IId<>)) continue;
+
+ foundInterface = iface;
+ break;
+ }
+
+ var idType = foundInterface?.GetGenericArguments()[0];
+ return (foundInterface != null && idType != null && idType.IsValueType, idType);
+ });
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Type? GetElementType(Type t)
+ {
+ return _collectionElemCache.GetOrAdd(t, static type =>
+ {
+ if (type.IsArray) return type.GetElementType();
+
+ var interfaces = type.GetInterfaces();
+ Type? ienum = null;
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ {
+ ienum = type;
+ }
+ else
+ {
+ for (var i = 0; i < interfaces.Length; i++)
+ {
+ var iface = interfaces[i];
+ if (!iface.IsGenericType || iface.GetGenericTypeDefinition() != typeof(IEnumerable<>)) continue;
+ ienum = iface;
+ break;
+ }
+ }
+
+ return ienum?.GetGenericArguments()[0];
+ });
+ }
+
+ // π OPTIMIZATION: Get fully cached property info with all computed values
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static CachedPropertyInfo[] GetCachedProperties(Type t)
+ {
+ return _cachedPropertyInfoCache.GetOrAdd(t, static type =>
+ {
+ var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ var cached = new CachedPropertyInfo[props.Length];
+ for (var i = 0; i < props.Length; i++)
+ {
+ cached[i] = new CachedPropertyInfo(props[i]);
+ }
+ return cached;
+ });
+ }
+
+ // π OPTIMIZATION: Cache JsonIgnore attribute check
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool HasJsonIgnoreAttribute(PropertyInfo prop)
+ {
+ return _jsonIgnoreCache.GetOrAdd(prop, static p =>
+ p.GetCustomAttribute() != null ||
+ p.GetCustomAttribute() != null);
+ }
+ }
+
+ public static class ReferenceRegistry
+ {
+ private const string ContextKey = "SemanticReferenceRegistry";
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary GetRegistry(JsonSerializer serializer)
+ {
+ if (serializer.Context.Context is not Dictionary