Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure CamelCasePropertyNamesContractResolver considers NamingStrategy in the cache #2930

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -240,5 +240,36 @@ public void DictionaryCamelCasePropertyNames()
""second"": ""Value2!""
}", json);
}

[Test]
public void EnsureNamingStrategyIsConsideredInCache()
{
Dictionary<string, string> values = new Dictionary<string, string> { { "Key", "Value" } };

string jsonWithProcessDictionaryKeys = JsonConvert.SerializeObject(values,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true }
}
});

StringAssert.AreEqual(@"{""key"":""Value""}", jsonWithProcessDictionaryKeys);


string jsonWithoutProcessDictionaryKeys = JsonConvert.SerializeObject(values,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = false }
}
});

StringAssert.AreEqual(@"{""Key"":""Value""}", jsonWithoutProcessDictionaryKeys);
}


}
}
Expand Up @@ -37,7 +37,7 @@ public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
private static readonly object TypeContractCacheLock = new object();
private static readonly DefaultJsonNameTable NameTable = new DefaultJsonNameTable();
private static Dictionary<StructMultiKey<Type, Type>, JsonContract>? _contractCache;
private static Dictionary<StructMultiKey<Type, Type, NamingStrategy?>, JsonContract>? _contractCache;

/// <summary>
/// Initializes a new instance of the <see cref="CamelCasePropertyNamesContractResolver"/> class.
Expand All @@ -64,8 +64,8 @@ public override JsonContract ResolveContract(Type type)
}

// for backwards compadibility the CamelCasePropertyNamesContractResolver shares contracts between instances
StructMultiKey<Type, Type> key = new StructMultiKey<Type, Type>(GetType(), type);
Dictionary<StructMultiKey<Type, Type>, JsonContract>? cache = _contractCache;
StructMultiKey<Type, Type, NamingStrategy?> key = new StructMultiKey<Type, Type, NamingStrategy?>(GetType(), type, NamingStrategy);
Dictionary<StructMultiKey<Type, Type, NamingStrategy?>, JsonContract>? cache = _contractCache;
if (cache == null || !cache.TryGetValue(key, out JsonContract? contract))
{
contract = CreateContract(type);
Expand All @@ -74,9 +74,9 @@ public override JsonContract ResolveContract(Type type)
lock (TypeContractCacheLock)
{
cache = _contractCache;
Dictionary<StructMultiKey<Type, Type>, JsonContract> updatedCache = (cache != null)
? new Dictionary<StructMultiKey<Type, Type>, JsonContract>(cache)
: new Dictionary<StructMultiKey<Type, Type>, JsonContract>();
Dictionary<StructMultiKey<Type, Type, NamingStrategy?>, JsonContract> updatedCache = (cache != null)
? new Dictionary<StructMultiKey<Type, Type, NamingStrategy?>, JsonContract>(cache)
: new Dictionary<StructMultiKey<Type, Type, NamingStrategy?>, JsonContract>();
updatedCache[key] = contract;

_contractCache = updatedCache;
Expand Down
34 changes: 34 additions & 0 deletions Src/Newtonsoft.Json/Utilities/StructMultiKey.cs
Expand Up @@ -58,4 +58,38 @@ public bool Equals(StructMultiKey<T1, T2> other)
return (Equals(Value1, other.Value1) && Equals(Value2, other.Value2));
}
}

internal readonly struct StructMultiKey<T1, T2, T3> : IEquatable<StructMultiKey<T1, T2, T3>>
{
public readonly T1 Value1;
public readonly T2 Value2;
public readonly T3 Value3;

public StructMultiKey(T1 v1, T2 v2, T3 v3)
{
Value1 = v1;
Value2 = v2;
Value3 = v3;
}

public override int GetHashCode()
{
return (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0) ^ (Value3?.GetHashCode() ?? 0);
}

public override bool Equals(object? obj)
{
if (!(obj is StructMultiKey<T1, T2, T3> key))
{
return false;
}

return Equals(key);
}

public bool Equals(StructMultiKey<T1, T2, T3> other)
{
return (Equals(Value1, other.Value1) && Equals(Value2, other.Value2) && Equals(Value3, other.Value3));
}
}
}