如何使用适当的大小写在 SignalR 中传递复杂对象?
How to pass complex objects in SignalR with proper casing?
我的 c# 中有一个像这样的复杂 class:
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; }
}
为了将它传递给我的客户,我这样做:
Clients.Caller.CheckVersion(ChannelInstance);
我的问题是,当我在我的客户端上收到对象时,它仍然会有 CamelCasing,而不是 camelCasing。有什么方法可以做到这一点,所以 SignalR 会自动将我的对象转换为具有适当可变大小写的对象?
我知道这是一件很琐碎的事情,但我发现在我的 javascript:
中定义这样的 class 很烦人
function Channel() {
this.Id;
this.ChannelName;
this.etc;
}
当这看起来更正确时 JavaScript:
function Channel() {
this.id;
this.channelName;
this.etc;
}
有什么办法可以做到这一点,还是我只能凑合使用奇怪的 CamelCasing?
不,您不能,当您使用 JsonSerializerSettings class 更改服务器上的默认 JSON.net 序列化设置时,SignalR jquery 客户端将停止工作,因为它希望通过使用默认的 JSON.net 序列化设置来序列化它的服务器消息。我相信在第 3 版中他们会改变这一点。
正如 Rob Segerink 在 , it's apparently not possible to change the global JsonSerializerSettings
without breaking SignalR. A quick search of the source 中指出的那样,它有时 new JsonSerializer()
有时 JsonSerializer.CreateDefault()
,这可能至少是部分问题的原因。
话虽如此,您也许可以采用问题 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()
{
}
}
接下来,使用此属性标记您的程序集、类型或接口以启用驼峰式大小写:
[assembly: MyNamespace.JsonCamelCaseAttribute]
最后,使用以下设置使用 中显示的技术安装合同解析器:
public static class ConverterSettings
{
public static JsonSerializer GetSerializer()
{
return JsonSerializer.Create(new JsonSerializerSettings()
{
ContractResolver = new ConditionalCamelCaseContractResolver()
});
}
}
由于SignalR自身的内部类型不会被如此标记,它们将继续使用默认设置进行序列化。
注意 - 使用各种测试用例进行了测试,但未对 SignalR 本身进行测试,因为我目前尚未安装它。
我知道这是一个老问题,但这个快速解决方案可能会帮助遇到此问题的人。它在过去确实对我有所帮助。
DataContract
and DataMember
属性可能正是您想要以您想要的方式序列化您的 class 并且在 C# 中仍然保持大写字母的东西。
您的 class 看起来像这样:
[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; }
...
}
这将按照您想要的方式对其进行序列化。
我的 c# 中有一个像这样的复杂 class:
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; }
}
为了将它传递给我的客户,我这样做:
Clients.Caller.CheckVersion(ChannelInstance);
我的问题是,当我在我的客户端上收到对象时,它仍然会有 CamelCasing,而不是 camelCasing。有什么方法可以做到这一点,所以 SignalR 会自动将我的对象转换为具有适当可变大小写的对象?
我知道这是一件很琐碎的事情,但我发现在我的 javascript:
中定义这样的 class 很烦人function Channel() {
this.Id;
this.ChannelName;
this.etc;
}
当这看起来更正确时 JavaScript:
function Channel() {
this.id;
this.channelName;
this.etc;
}
有什么办法可以做到这一点,还是我只能凑合使用奇怪的 CamelCasing?
不,您不能,当您使用 JsonSerializerSettings class 更改服务器上的默认 JSON.net 序列化设置时,SignalR jquery 客户端将停止工作,因为它希望通过使用默认的 JSON.net 序列化设置来序列化它的服务器消息。我相信在第 3 版中他们会改变这一点。
正如 Rob Segerink 在 JsonSerializerSettings
without breaking SignalR. A quick search of the source 中指出的那样,它有时 new JsonSerializer()
有时 JsonSerializer.CreateDefault()
,这可能至少是部分问题的原因。
话虽如此,您也许可以采用问题
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()
{
}
}
接下来,使用此属性标记您的程序集、类型或接口以启用驼峰式大小写:
[assembly: MyNamespace.JsonCamelCaseAttribute]
最后,使用以下设置使用
public static class ConverterSettings
{
public static JsonSerializer GetSerializer()
{
return JsonSerializer.Create(new JsonSerializerSettings()
{
ContractResolver = new ConditionalCamelCaseContractResolver()
});
}
}
由于SignalR自身的内部类型不会被如此标记,它们将继续使用默认设置进行序列化。
注意 - 使用各种测试用例进行了测试,但未对 SignalR 本身进行测试,因为我目前尚未安装它。
我知道这是一个老问题,但这个快速解决方案可能会帮助遇到此问题的人。它在过去确实对我有所帮助。
DataContract
and DataMember
属性可能正是您想要以您想要的方式序列化您的 class 并且在 C# 中仍然保持大写字母的东西。
您的 class 看起来像这样:
[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; }
...
}
这将按照您想要的方式对其进行序列化。