【问题标题】:Unable to deserialize polymorphic dictionary json due to KnownType "__type" issue由于 KnownType“__type”问题,无法反序列化多态字典 json
【发布时间】:2020-03-09 04:03:41
【问题描述】:

我创建了一个包含多态值的字典,其中保存了一个类对象。我已成功序列化 JSON。但我无法反序列化它。它给出了以下错误:

元素 ':Value' 包含 ':Sale' 数据合约的数据。反序列化器不知道任何映射到该合约的类型。

如果将 JSON 属性 "__type" 替换为 "type",则它可以工作,但无法恢复正确的对象类型。在序列化之前,它包含我的类类型的对象,但在反序列化之后,它包含 system.object

我的代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        dict.Add("employee","john");
        dict.Add("sale",new Sale(9,5243));
        dict.Add("restaurant",new Restaurant("Cheese Cake Factory", "New York"));
        //  Console.Write(dict["sale"]);

        //Code for JSON
        DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Dictionary<string, object>));  
        MemoryStream msObj = new MemoryStream();  
        js.WriteObject(msObj, dict);  
        msObj.Position = 0;  
        StreamReader sr = new StreamReader(msObj);  
        string json = sr.ReadToEnd();  
        sr.Close();  
        msObj.Close();

        // Decode the thing
        Console.Write(json);

        Dictionary<string, object> result = new Dictionary<string, object>();
        using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary<string, object>));
            result = serializer.ReadObject(stream) as Dictionary<string, object>;
        }
    }
}

[DataContract]
[KnownType(typeof(Sale))]
public class Sale 
{
    [DataMember(Name = "SaleId")]
    public int SaleId {get; set;}
    [DataMember(Name = "Total")]
    public int Total{ get; set;}

    public Sale(int saleid, int total)
    {
        SaleId = saleid;
        Total = total;
    }

    public int getTotal()
    {
        return  Total;
    }
}

[DataContract(Name = "Restaurant", Namespace="")]
[KnownType(typeof(Restaurant))]
public class Restaurant
{
    [DataMember(EmitDefaultValue = false)]
    public string Name {get; set;}
    [DataMember(EmitDefaultValue = false)]
    public string City{get; set;}

    public Restaurant(string name, string city)
    {
        Name = name;
        City = city;
    }
}

小提琴链接:https://dotnetfiddle.net/CfQxxV

【问题讨论】:

    标签: c# .net types polymorphism datacontractjsonserializer


    【解决方案1】:

    您遇到的问题是您将[KnownType(typeof(...))] 放在saleRestaurant 之上。

    使用 KnownType 的原因是为了在 1 和另一个对象之间进行转换。所以解串器不知道SaleObject 的KnownType。所以它不能将对象转换为销售。

    这只有在字典中的所有项目都共享一个像这样的公共父对象时才有效:

        [KnownType(typeof(Sale))]
        [KnownType(typeof(Restaurant))]
        [KnownType(typeof(Employee))]
        [DataContract]
        public class SomeObject {
    
        }
    
        [DataContract(Name = "Sale", Namespace="")]
        public class Sale : SomeObject
        {
           //methods + properties + variables
        }
    
        [DataContract(Name = "Restaurant", Namespace="")]
        public class Restaurant : SomeObject
        {
            //methods + properties + variables   
        }
    
        [DataContract(Name = "Employee", Namespace="")]
        public class Employee: SomeObject
        {
            //methods + properties + variables   
        }
    
    

    然后用字典作为

    Dictionary<string, SomeObject> dict = new Dictionary<string, SomeObject>();
    

    【讨论】:

    • 感谢您的快速解决方案。它工作正常。我已经更新了小提琴。但我无法添加非对象。那么你有什么想法吗?这样我也可以在下面添加。 dict.Add("员工","约翰");
    • 它还需要是一个扩展 SomeObject 的对象,我稍微编辑了我的答案
    【解决方案2】:

    您正在尝试使用多态成员序列化根对象Dictionary&lt;string, object&gt;。数据协定序列化程序使用白名单方法来处理多态性:在序列化过程中遇到的所有多态子类型必须在遇到该子类型的实例之前通过known type 机制预先声明在序列化图中。

    那么,如何使用您的数据模型来做到这一点?有几种方法:

    1. [KnownType(typeof(TDerivedObject))] 直接添加到由Joost Kthis answer 中显示的静态声明的基类型中。

      这不能在这里工作,因为基本类型是object,你不能修改它。

    2. [KnownType(typeof(TDerivedObject))] 添加到序列化图中的某个父对象

      这看起来有问题,因为您的根对象类型是 Dictionary&lt;string, object&gt;,但是您可以对字典进行子类化以获得所需的结果:

      [KnownType(typeof(Sale))]
      [KnownType(typeof(Restaurant))]
      public class ObjectDictionary : Dictionary<string, object>
      {
      }
      

      然后使用这个子类构造和序列化你的字典:

      var dict = new ObjectDictionary()
      {
          { "employee","john" },
          {"sale",new Sale(9,5243) },
          {"restaurant",new Restaurant("Cheese Cake Factory", "New York")},
      };
      
      DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(ObjectDictionary));  
      

      演示小提琴 #1 here.

    3. 通过constructing 使用DataContractJsonSerializerSettingsDataContractJsonSerializerSettings.KnownTypes 中指定的已知类型的序列化程序在运行时配置其他已知类型:

      var settings = new DataContractJsonSerializerSettings
      {
          KnownTypes = new [] { typeof(Sale), typeof(Restaurant) },
      };
      DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Dictionary<string, object>), settings);  
      

      确保为序列化和反序列化以相同的方式配置序列化程序。

      演示小提琴 #2 here.

      (对于 XML,请使用 DataContractSerializerSettings.KnownTypes。)

    4. 通过配置文件指定其他已知类型,如 Additional Ways to Add Known Types 所示。

    5. 您正在直接进行序列化,但如果您通过 WCF 进行序列化,则可以将 ServiceKnownTypeAttribute 添加到您的服务合同中。

    永远不需要将[KnownType(typeof(TClass))] 添加到TClass 本身,因此您可以从RestaurantSale 中删除此类属性:

    [DataContract]
    //[KnownType(typeof(Sale))] Remove this
    public class Sale 
    {
        // Remainder unchanged
    }
    

    更多信息,请参阅Sowmy SrinivasanAll About KnownTypes

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-19
      • 2019-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多