我正在尝试让SignalR与自定义JsonSerializerSettings一起处理其有效负载,特别是我正在尝试设置TypeNameHandling = TypeNameHandling.Auto。
TypeNameHandling = TypeNameHandling.Auto
这个问题似乎是,该SignalR中使用的设置hubConnection.JsonSerializer,并GlobalHost.DependencyResolver.Resolve<JsonSerializer>()为它的内部数据结构以及它然后导致各种混乱的(当我设置内部服务器崩溃TypeNameHandling.All的最粗鲁的例子,但TypeNameHandling.Auto我也得问题,特别是当IProgress<>回调参与)。
hubConnection.JsonSerializer
GlobalHost.DependencyResolver.Resolve<JsonSerializer>()
TypeNameHandling.All
TypeNameHandling.Auto
IProgress<>
有任何解决方法,还是我做错了?
示例代码演示:
服务器:
class Program { static void Main(string[] args) { using (WebApp.Start("http://localhost:8080")) { Console.ReadLine(); } } } public class Startup { public void Configuration(IAppBuilder app) { var hubConfig = new HubConfiguration() { EnableDetailedErrors = true }; GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), ConverterSettings.GetSerializer); app.MapSignalR(hubConfig); } } public interface IFoo { string Val { get; set; } } public class Foo : IFoo { public string Val { get; set; } } public class MyHub : Hub { public IFoo Send() { return new Foo { Val = "Hello World" }; } }
客户:
class Program { static void Main(string[] args) { Task.Run(async () => await Start()).Wait(); } public static async Task Start() { var hubConnection = new HubConnection("http://localhost:8080"); hubConnection.JsonSerializer = ConverterSettings.GetSerializer(); var proxy = hubConnection.CreateHubProxy("MyHub"); await hubConnection.Start(); var result = await proxy.Invoke<IFoo>("Send"); Console.WriteLine(result.GetType()); }
共享:
public static class ConverterSettings { public static JsonSerializer GetSerializer() { return JsonSerializer.Create(new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); } }
这可以通过利用您的类型和SignalR类型在不同的程序集中的事实来完成。这个想法是要创建一个JsonConverter适用于 程序集中所有类型的 。当在对象图中(可能是根对象)首先遇到某个程序集中jsonSerializer.TypeNameHandling = TypeNameHandling.Auto的类型时,转换器将临时设置,然后对该类型进行标准序列化,并在一段时间内禁用自身以防止无限递归:
JsonConverter
jsonSerializer.TypeNameHandling = TypeNameHandling.Auto
public class PolymorphicAssemblyRootConverter : JsonConverter { [ThreadStatic] static bool disabled; // Disables the converter in a thread-safe manner. bool Disabled { get { return disabled; } set { disabled = value; } } public override bool CanWrite { get { return !Disabled; } } public override bool CanRead { get { return !Disabled; } } readonly HashSet<Assembly> assemblies; public PolymorphicAssemblyRootConverter(IEnumerable<Assembly> assemblies) { if (assemblies == null) throw new ArgumentNullException(); this.assemblies = new HashSet<Assembly>(assemblies); } public override bool CanConvert(Type objectType) { return assemblies.Contains(objectType.Assembly); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters using (new PushValue<TypeNameHandling>(TypeNameHandling.Auto, () => serializer.TypeNameHandling, val => serializer.TypeNameHandling = val)) { return serializer.Deserialize(reader, objectType); } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters using (new PushValue<TypeNameHandling>(TypeNameHandling.Auto, () => serializer.TypeNameHandling, val => serializer.TypeNameHandling = val)) { // Force the $type to be written unconditionally by passing typeof(object) as the type being serialized. serializer.Serialize(writer, value, typeof(object)); } } } public struct PushValue<T> : IDisposable { Action<T> setValue; T oldValue; public PushValue(T value, Func<T> getValue, Action<T> setValue) { if (getValue == null || setValue == null) throw new ArgumentNullException(); this.setValue = setValue; this.oldValue = getValue(); setValue(value); } #region IDisposable Members // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class. public void Dispose() { if (setValue != null) setValue(oldValue); } #endregion }
然后在启动时,将此转换器添加到默认转换器中JsonSerializer,传入要"$type"应用的程序集。
JsonSerializer
"$type"
更新资料
如果出于某种原因在启动时不方便传递程序集列表,则可以通过启用转换器objectType.Namespace。居住在指定名称空间中的所有类型都将自动使用序列化TypeNameHandling.Auto。
objectType.Namespace
或者,您可以引入Attribute其目标的组件,类或接口,使TypeNameHandling.Auto当与适当的转换器结合使用:
Attribute
public class EnableJsonTypeNameHandlingConverter : JsonConverter { [ThreadStatic] static bool disabled; // Disables the converter in a thread-safe manner. bool Disabled { get { return disabled; } set { disabled = value; } } public override bool CanWrite { get { return !Disabled; } } public override bool CanRead { get { return !Disabled; } } public override bool CanConvert(Type objectType) { if (Disabled) return false; if (objectType.Assembly.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>().Any()) return true; if (objectType.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>(true).Any()) return true; foreach (var type in objectType.GetInterfaces()) if (type.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>(true).Any()) return true; return false; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters using (new PushValue<TypeNameHandling>(TypeNameHandling.Auto, () => serializer.TypeNameHandling, val => serializer.TypeNameHandling = val)) { return serializer.Deserialize(reader, objectType); } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters using (new PushValue<TypeNameHandling>(TypeNameHandling.Auto, () => serializer.TypeNameHandling, val => serializer.TypeNameHandling = val)) { // Force the $type to be written unconditionally by passing typeof(object) as the type being serialized. serializer.Serialize(writer, value, typeof(object)); } } } [System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Interface)] public class EnableJsonTypeNameHandlingAttribute : System.Attribute { public EnableJsonTypeNameHandlingAttribute() { } }
注意-已通过各种测试用例进行了测试,但未对SignalR本身进行过测试,因为我目前尚未安装它。
TypeNameHandling 警告
TypeNameHandling
使用时TypeNameHandling,请注意Newtonsoft文档中的这一警告:
当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。反序列化除None以外的其他值时,应使用自定义SerializationBinder验证传入的类型。