Newtonsoft.json对来自共享项目的类进行序列化和反序列化 [英] Newtonsoft.json serializing and deserializing base/inheirited where classes are from shared projects

查看:216
本文介绍了Newtonsoft.json对来自共享项目的类进行序列化和反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有两个像下面的类.它们都在相同的名称空间和相同的共享项目中.

So I have two classes like the ones below. They are both in the same namespace and in the same shared project.

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

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

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

When I serilize these items into rabbitmq I am serializing as the base class like so:

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

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

However when deserializing I run into issues. I would like to be able to do something like shown below where I deserialize as the base class and then check if it is a inheirited type.

类型检查:

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

Result: The above code fails because the $type property contains the Assembly name and on each end of rabbitmq the assembly name is different because the classes are inside a shared project.

示例错误:

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是解决问题的最佳方法.

Thank you @dbc, your suggestion to write a custom SerializationBinder is, as far as I can tell, the best solution to my problem.

我使用了在以下位置实现的KnownTypesBinder: https://www.newtonsoft .com/json/help/html/SerializeSerializationBinder.htm

I used the KnownTypesBinder as implemented at: https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm

KnownTypesBinder:

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.我可能只需要将其用于反序列化即可,但是为了保持一致性,请将两者放在一起.

JsonSerializerSettings with the SerializationBinder set to an instance of KnownTypesBinder was used on both the serializing and deserializing endpoints. I probably only need it for the deserializing end, but put it in both for consistency.

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

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

After creating a settings object then I pass it into the JsonConvert serialization functions.

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

还请注意,KnownTypesBinder中的KnownTypes必须预填充要反序列化的所有非原始类型.

Also note that KnownTypes in KnownTypesBinder must be prepopulated with all of the non primitive types you will be deserializing.

修改: 我目前不接受自己的答案,因为我不知道如何处理复杂类型列表.例如,如果一个人有一个列表和一个列表,则当typeName为"List`1"且可能是任一类型时,您将返回什么类型.

I am currently not accepting my own answer because I have no idea how to handle List of complex types. For instance if a Person has a List and a List, what type do you return when the typeName is "List`1" and it could be either one.

修改 以下版本的KnownTypesBinder解决了我与对象列表有关的问题.

Edit The following version of the KnownTypesBinder solved my issues related to Lists of objects.

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();
    }
}

这篇关于Newtonsoft.json对来自共享项目的类进行序列化和反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆