2

I have a complex class like this in my c#:

public class Channel
{
    public int Id { get; set; }
    public string ChannelName { get; set; }
    public Dictionary<int, Question> Questions { get; set; }
    public Dictionary<int, ChatMessage> ChatMessages { get; set; }
    public Dictionary<int, User> Users { get; set; }
    public bool IsAdmin { get; set; }
    public int TimeLeft { get; set; }
}

To pass it to my client i do: Clients.Caller.CheckVersion(ChannelInstance);

My problem is that when i recieve the object on my client it will still have CamelCasing, instead of camelCasing. Is there any way to do this, so SignalR will automatically convert my object into an object with proper variable casing?

I know it's a pretty petty something, but i find it pretty annoying to have a class defined like this in my javascript:

function Channel() {
    this.Id;
    this.ChannelName;
    this.etc;
}

when this looks much more JavaScript correct:

function Channel() {
    this.id;
    this.channelName;
    this.etc;
}

Is there any way to do this, or will I just have to make do with the odd CamelCasing?

Rasmus Hansen
  • 1,502
  • 1
  • 17
  • 28

3 Answers3

1

No, you can't, when you change the default JSON.net serialize settings on the server, by using the JsonSerializerSettings class, the SignalR jquery client will stop working because it expects it's server messages to be serialized by using the default JSON.net serialize settings. I believe in version 3 they will change this.

Rob Segerink
  • 106
  • 1
  • 3
1

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
0

I know this is an old question but this quick solution might help someone coming across this problem. It certainly has helped me in the past.

The DataContract and DataMember attributes might be exactly what your a looking for to serialize your class in the way you want and still keep it upper case letter in C#.

Your class would look like this:

[DataContract]
public class Channel
{
    [DataMember(Name = "id")]
    public int Id { get; set; }
    [DataMember(Name = "channelName")]
    public string ChannelName { get; set; }
    [DataMember(Name = "questions")]
    public Dictionary<int, Question> Questions { get; set; }
    
    ...
}

This will serialize it just the way you want it.

Matthias
  • 75
  • 8