【问题标题】:Newtonsoft.json serializing and deserializing base/inheirited where classes are from shared projectsNewtonsoft.json 序列化和反序列化基/继承,其中类来自共享项目
【发布时间】:2018-02-05 23:16:51
【问题描述】:

所以我有两个类,如下所示。它们都在同一个命名空间和同一个共享项目中。

public class Person{
   public string Name{get;set;}
}

public class EmployedPerson : Person{
   public string JobTitle{get;set;}
}

当我将这些项目序列化到rabbitmq中时,我将序列化为基类,如下所示:

JsonSerializerSettings settings = new JsonSerializerSettings
{
   TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
   TypeNameHandling = TypeNameHandling.Objects
};
JsonConvert.SerializeObject(input, settings)

但是在反序列化时我遇到了问题。我希望能够执行如下所示的操作,我将反序列化为基类,然后检查它是否是继承类型。

类型检查:

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 的每一端,程序集名称不同,因为类位于共享项目中。

示例错误:

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.

【问题讨论】:

  • 我认为这可能是您问题的答案:stackoverflow.com/questions/12381636/…
  • 你可以 1) 写一个custom SerializationBinder。正如here 所解释的那样,出于安全原因编写自己的对反序列化类型进行清理的活页夹也是一个好主意。 2) 发出您自己的自定义类型属性并使用自定义JsonConverter 对其进行解析,如图所示,例如在Json.Net Serialization of Type with Polymorphic Child Object 中。
  • 查看 SerializationBinder。
  • 我很困惑。如果类型在共享项目中,为什么队列两端的程序集名称不同?
  • 因为共享项目不创建自己的程序集。它们被同化到正在使用它的项目中。

标签: c# json serialization json.net shared-project


【解决方案1】:

谢谢@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;
        }
    }

将 SerializationBinder 设置为 KnownTypesBinder 实例的 JsonSerializerSettings 用于序列化和反序列化端点。我可能只需要它用于反序列化端,但为了保持一致性,将它放在两者中。

settings = new JsonSerializerSettings
{
    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
    TypeNameHandling = TypeNameHandling.Objects,
    SerializationBinder = new KnownTypesBinder()
};

创建设置对象后,我将其传递给 JsonConvert 序列化函数。

JsonConvert.DeserializeObject<T>(Encoding.Unicode.GetString(input), settings) 

另请注意,KnownTypesBinder 中的 KnownTypes 必须预先填充您将要反序列化的所有非原始类型。

编辑: 我目前不接受我自己的答案,因为我不知道如何处理复杂类型的列表。例如,如果一个 Person 有一个 List 和一个 List,当 typeName 为“List`1”时你返回什么类型,它可以是任何一个。

编辑 以下版本的 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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    相关资源
    最近更新 更多