As Rob Segerink states in this answer, it's apparently not possible to change the global JsonSerializerSettings
without breaking SignalR. A quick search of the source reveals that it sometimes does new JsonSerializer()
and sometimes JsonSerializer.CreateDefault()
, which might be causing at least part of the problem.
That being said, you may be able to adopt the trick from the question SignalR Typenamehandling to your needs, in particular to override Json.NET's behavior and use camel casing only for types marked with a specific attribute, or in assemblies marked with a specific attribute, using the following contract resolver:
public sealed class ConditionalCamelCaseContractResolver : IContractResolver
{
readonly static IContractResolver defaultContractResolver;
readonly static IContractResolver camelCaseContractResolver;
readonly static ConcurrentDictionary<Type, bool> camelCaseTable;
static Func<Type, bool> isCamelCase;
// Use a static constructor for lazy initialization.
static ConditionalCamelCaseContractResolver()
{
defaultContractResolver = new JsonSerializer().ContractResolver; // This seems to be the only way to access the global singleton default contract resolver.
camelCaseContractResolver = new CamelCasePropertyNamesContractResolver();
camelCaseTable = new ConcurrentDictionary<Type, bool>();
isCamelCase = (t) => GetIsCamelCase(t);
}
static bool GetIsCamelCase(Type objectType)
{
if (objectType.Assembly.GetCustomAttributes<JsonCamelCaseAttribute>().Any())
return true;
if (objectType.GetCustomAttributes<JsonCamelCaseAttribute>(true).Any())
return true;
foreach (var type in objectType.GetInterfaces())
if (type.GetCustomAttributes<JsonCamelCaseAttribute>(true).Any())
return true;
return false;
}
static bool IsCamelCase(Type objectType)
{
var code = Type.GetTypeCode(objectType);
if (code != TypeCode.Object && code != TypeCode.Empty)
return false;
return camelCaseTable.GetOrAdd(objectType, isCamelCase);
}
#region IContractResolver Members
public JsonContract ResolveContract(Type type)
{
return IsCamelCase(type) ? camelCaseContractResolver.ResolveContract(type) : defaultContractResolver.ResolveContract(type);
}
#endregion
}
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Interface)]
public class JsonCamelCaseAttribute : System.Attribute
{
public JsonCamelCaseAttribute()
{
}
}
Next, mark your assemblies, types or interfaces with this attribute to enable camel casing:
[assembly: MyNamespace.JsonCamelCaseAttribute]
Finally, install the contract resolver with the techniques shown in that question using the following settings:
public static class ConverterSettings
{
public static JsonSerializer GetSerializer()
{
return JsonSerializer.Create(new JsonSerializerSettings()
{
ContractResolver = new ConditionalCamelCaseContractResolver()
});
}
}
Since SignalR's own internal types will not be so marked, they will continue to be serialized using default settings.
Note - tested with various test cases but not SignalR itself since I don't currently have it installed.