This can be done by taking advantage of the fact that your types and the SignalR types are in different assemblies. The idea is to create a JsonConverter that applies to all types from your assemblies. When a type from one of your assemblies is first encountered in the object graph (possibly as the root object), the converter would temporarily set jsonSerializer.TypeNameHandling = TypeNameHandling.Auto, then proceed with the standard serialization for the type, disabling itself for the duration to prevent infinite recursion: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" 的程序集.Then in startup you would add this converter to the default JsonSerializer, passing in the assemblies for which you want "$type" applied.更新如果由于某种原因在启动时传递程序集列表不方便,您可以通过 objectType.Namespace.位于指定命名空间中的所有类型都将使用 TypeNameHandling.Auto 自动序列化.If for whatever reason it's inconvenient to pass the list of assemblies in at startup, you could enable the converter by objectType.Namespace. All types living in your specified namespaces would automatically get serialized with TypeNameHandling.Auto.或者,您可以引入一个 Attribute,其中 目标一个程序集、类或接口,并在与适当的转换器结合时启用TypeNameHandling.Auto:Alternatively, you could introduce an Attribute which targets an assembly, class or interface and enables TypeNameHandling.Auto when combined with the appropriate converter: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 本身进行测试,因为我目前没有安装它.Note - tested with various test cases but not SignalR itself since I don't currently have it installed.TypeNameHandling 注意TypeNameHandling Caution在使用 TypeNameHandling 时,请注意 Newtonsoft 文档:When using TypeNameHandling, do take note of this caution from the Newtonsoft docs:当您的应用程序从外部源反序列化 JSON 时,应谨慎使用 TypeNameHandling.使用 None 以外的值反序列化时,应使用自定义 SerializationBinder 验证传入类型. TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.有关为什么需要这样做的讨论,请参阅Newtonsoft Json 中的 TypeNameHandling 警告.For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json. 这篇关于SignalR 类型名称处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
05-30 02:43