【问题标题】:Assign/Deserialize generic object with more derived type into the one with less derived type将具有更多派生类型的通用对象分配/反序列化为具有较少派生类型的对象
【发布时间】:2016-03-27 10:57:29
【问题描述】:

我有一个具有以下结构的类

public class GenericEntity<T>
    where T : BaseClass
{
    T Property {get; set;}
}

我有几个BaseClass 的具体实现,我正在使用这些具体实现来实例化GenericEntity 类型的对象

例如var entity = new GenericEntity&lt;DerivedClass&gt;()

我通过将对象序列化为 JSON(使用 Newtonsoft)将这个实体推送到消息传递框架中。在另一端,我正在从消息队列中提取该消息 (JSON),并尝试使用 JsonSerializerSettings TypeNameHandling.All 将消息反序列化为 GenericEntity&lt;BaseClass&gt; 类型。但是在反序列化的时候,会抛出JsonSerializationException的细节

在 JSON 中指定的类型 'NewtonsoftPlayground.GenericEntity1[[NewtonsoftPlayground.DerivedClass, NewtonsoftPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], NewtonsoftPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with 'NewtonsoftPlayground.GenericEntity1[[NewtonsoftPlayground.BaseClass, NewtonsoftPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], NewtonsoftPlayground, Version=1.0.0.0, Culture=neutral , PublicKeyToken=null'。

我怎样才能做到这一点?还是我不应该使用泛型?如果是这样,我会在从队列中提取消息后到处写类型转换,这是我试图避免的。

【问题讨论】:

    标签: c# generics json.net covariance


    【解决方案1】:

    我重新创建了以下代码:

    public class GenericEntity<T> where T : BaseClass 
        {
       public T Property { get; set; }
    }
    
    public class BaseClass {
    }
    
    class Derived1 : BaseClass
    {
        public int Age { get; set; }
    }
    
    class Derived2 : BaseClass {
        public string Name { get; set; }
    }
    
    ....
    
    static void Main()
        {
            Derived1 d1 = new Derived1 {Age = 23};
            GenericEntity<Derived1> entity = new GenericEntity<Derived1> {Property = d1};
    
            var data = JsonConvert.SerializeObject(entity, new JsonSerializerSettings() {
                TypeNameHandling = TypeNameHandling.All
            });
            var baseEntity = JsonConvert.DeserializeObject(data, typeof(GenericEntity<BaseClass>));
    }
    

    我在反序列化数据时没有收到任何错误。让我知道您的上下文以及它的不同之处。

    这显然行不通:

            BaseClass d1 = new Derived1 {Age = 23};
            GenericEntity<BaseClass> entity = new GenericEntity<BaseClass> {Property = d1};
    
            var data = JsonConvert.SerializeObject(entity, new JsonSerializerSettings() {
                TypeNameHandling = TypeNameHandling.All
            });
            var baseEntity = JsonConvert.DeserializeObject<GenericEntity<Derived1>>(data, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All
            });
    

    如果您序列化 BaseClass,它将不负责序列化专用类所具有的所有额外细节(我的意思是,您想要序列化 ​​BaseClass,而这正是您要序列化的内容)。

    将它们序列化为专用实例是您的工作:也就是说,如果您获得一个 BaseClass 并且您知道需要 Derived1 将该实例解析为 Derived1,然后将其序列化。

    【讨论】:

    • 嗨@Tamas Ionut,如果您在序列化时使用TypeNameHandling.All 设置,您将收到错误消息。在您尝试的代码中,它将对象反序列化为基类,但缺少派生类属性值。我想要实现的是,反序列化的实例应该是Derived1 类型,但接口应该是BaseClass 类型。我希望这能进一步清除。谢谢。
    • 我同意你的看法。我只序列化专门的课程。我正在序列化GenericEntity&lt;Derived1&gt; 类型的实例,但在反序列化时我希望它是GenericEntity&lt;BaseClass&gt; 持有GenericEntity&lt;DerivedClass&gt; 类型的实例。
    • 我正在努力实现这一目标:` Derived1 d1 = new Derived1 { Age = 23 }; GenericEntity 实体 = 新的 GenericEntity { 属性 = d1 }; var data = JsonConvert.SerializeObject(entity, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); var baseEntity = JsonConvert.DeserializeObject>(data, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });`
    • 这仍然有问题,因为您指定 GenericEntity 进行反序列化,您将得到您所要求的。您可以通过指定具体类型(编辑答案)来尝试反序列化的非通用版本。这适用于您在编译时不知道类型的情况。
    • 明白了。即使使用带有typeof(GenericEntity&lt;BaseClass&gt;) 参数的非泛型方法也会引发不兼容的类型异常。有一个使用反射的解决方法。我在下面发布我的解决方案作为答案。
    【解决方案2】:

    我找不到任何直接的解决方案来实现这一点。正如@Tamas Ionut 建议的那样,在反序列化时指定基类不会反序列化具体类的属性。

    作为一种解决方法,我创建了一个静态方法,它基本上实例化 GenericEntity&lt;BaseClass&gt; 并使用反射复制其中的所有属性。

        public static GenericEntity<BaseClass> ConvertToBaseEntity(object model)
        {
            if (model.GetType().GetGenericTypeDefinition() != typeof(GenericEntity<>))
            {
                throw new ArgumentException(
                    "Model should be of type GenericEntity<T>.", "model");
            }
    
            GenericEntity<BaseClass> baseModel = new GenericEntity<BaseClass>();
    
            foreach (var propertyInfo in model.GetType().GetProperties())
            {
                baseModel.GetType().GetProperty(propertyInfo.Name).SetValue(baseModel, propertyInfo.GetValue(model));
            }
    
            return baseModel;
        }
    

    我希望,这对某人有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多