Newtonsoft.json 序列化和反序列化 base/inheirited 其中 类 来自共享项目
Newtonsoft.json serializing and deserializing base/inheirited where classes are from shared projects
所以我有两个 class 如下所示。它们都在同一个命名空间和同一个共享项目中。
public class Person{
public string Name{get;set;}
}
public class EmployedPerson : Person{
public string JobTitle{get;set;}
}
当我将这些项目序列化到 rabbitmq 中时,我将序列化为基础 class,如下所示:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects
};
JsonConvert.SerializeObject(input, settings)
但是在反序列化时我 运行 遇到了问题。我希望能够执行如下所示的操作,其中我将反序列化为基础 class,然后检查它是否是继承类型。
类型检查:
Person person = Deserialize<Person>(e.Body, Encoding.Unicode);
if (person is EmployedPerson)
{
logger.LogInformation("This person has a job!");
}
反序列化设置:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Auto
};
反序列化逻辑:
private static T Deserialize<T>(byte[] data, Encoding encoding) where T : class
{
try
{
using (MemoryStream stream = new MemoryStream(data))
using (StreamReader reader = new StreamReader(stream, encoding))
return JsonSerializer.Create(settings).Deserialize(reader, typeof(T)) as T;
}
catch (Exception e)
{
Type typeParameter = typeof(T);
logger.LogError(LogEvent.SERIALIZATION_ERROR, e, "Deserializing type {@TypeName} failed", typeParameter.Name);
logger.LogInformation(Encoding.UTF8.GetString(data));
return default(T);
}
}
结果:
上面的代码失败是因为 $type 属性 包含程序集名称,并且在 rabbitmq 的每一端程序集名称是不同的,因为 classes 在共享项目中。
示例错误:
Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'Shared.Objects.EmployedPerson, Person.Dispatcher'. Path '$type', line 1, position 75. ---> System.IO.FileNotFoundException: Could not load file or assembly 'Person.Dispatcher, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
谢谢@dbc,据我所知,您关于编写自定义 SerializationBinder 的建议是解决我的问题的最佳方法。
我使用了 KnownTypesBinder,实现于:https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm
KnownTypesBinder:
public class KnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
序列化端点和反序列化端点都使用了 JsonSerializerSettings,其中 SerializationBinder 设置为 KnownTypesBinder 的一个实例。我可能只在反序列化端需要它,但为了一致性将它放在两者中。
settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = new KnownTypesBinder()
};
创建设置对象后,我将其传递给 JsonConvert 序列化函数。
JsonConvert.DeserializeObject<T>(Encoding.Unicode.GetString(input), settings)
另请注意,KnownTypesBinder 中的 KnownTypes 必须预先填充您将反序列化的所有非基本类型。
编辑:
我目前不接受我自己的回答,因为我不知道如何处理复杂类型的列表。例如,如果一个人有一个列表和一个列表,当类型名称是 "List`1" 时,你 return 是什么类型,它可以是任何一个。
编辑
以下版本的 KnownTypesBinder 解决了我与对象列表相关的问题。
public class KnownTypesBinder: ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.UnderlyingSystemType.ToString() == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.UnderlyingSystemType.ToString();
}
}
所以我有两个 class 如下所示。它们都在同一个命名空间和同一个共享项目中。
public class Person{
public string Name{get;set;}
}
public class EmployedPerson : Person{
public string JobTitle{get;set;}
}
当我将这些项目序列化到 rabbitmq 中时,我将序列化为基础 class,如下所示:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects
};
JsonConvert.SerializeObject(input, settings)
但是在反序列化时我 运行 遇到了问题。我希望能够执行如下所示的操作,其中我将反序列化为基础 class,然后检查它是否是继承类型。
类型检查:
Person person = Deserialize<Person>(e.Body, Encoding.Unicode);
if (person is EmployedPerson)
{
logger.LogInformation("This person has a job!");
}
反序列化设置:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Auto
};
反序列化逻辑:
private static T Deserialize<T>(byte[] data, Encoding encoding) where T : class
{
try
{
using (MemoryStream stream = new MemoryStream(data))
using (StreamReader reader = new StreamReader(stream, encoding))
return JsonSerializer.Create(settings).Deserialize(reader, typeof(T)) as T;
}
catch (Exception e)
{
Type typeParameter = typeof(T);
logger.LogError(LogEvent.SERIALIZATION_ERROR, e, "Deserializing type {@TypeName} failed", typeParameter.Name);
logger.LogInformation(Encoding.UTF8.GetString(data));
return default(T);
}
}
结果: 上面的代码失败是因为 $type 属性 包含程序集名称,并且在 rabbitmq 的每一端程序集名称是不同的,因为 classes 在共享项目中。
示例错误:
Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'Shared.Objects.EmployedPerson, Person.Dispatcher'. Path '$type', line 1, position 75. ---> System.IO.FileNotFoundException: Could not load file or assembly 'Person.Dispatcher, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
谢谢@dbc,据我所知,您关于编写自定义 SerializationBinder 的建议是解决我的问题的最佳方法。
我使用了 KnownTypesBinder,实现于:https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm
KnownTypesBinder:
public class KnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
序列化端点和反序列化端点都使用了 JsonSerializerSettings,其中 SerializationBinder 设置为 KnownTypesBinder 的一个实例。我可能只在反序列化端需要它,但为了一致性将它放在两者中。
settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = new KnownTypesBinder()
};
创建设置对象后,我将其传递给 JsonConvert 序列化函数。
JsonConvert.DeserializeObject<T>(Encoding.Unicode.GetString(input), settings)
另请注意,KnownTypesBinder 中的 KnownTypes 必须预先填充您将反序列化的所有非基本类型。
编辑: 我目前不接受我自己的回答,因为我不知道如何处理复杂类型的列表。例如,如果一个人有一个列表和一个列表,当类型名称是 "List`1" 时,你 return 是什么类型,它可以是任何一个。
编辑 以下版本的 KnownTypesBinder 解决了我与对象列表相关的问题。
public class KnownTypesBinder: ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.UnderlyingSystemType.ToString() == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.UnderlyingSystemType.ToString();
}
}