关于 c#:由于 KnownType \\”__type\\” 问题,无法反序列化多态字典 json

Unable to deserialize polymorphic dictionary json due to KnownType "__type" issue

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

Element ':Value' contains data of the ':Sale' data contract. The deserializer has no knowledge of any type that maps to this contract.

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

我的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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


您正在尝试使用多态成员序列化根对象 Dictionary<string, object>。数据契约序列化程序使用白名单方法来处理多态性:在序列化过程中遇到的所有多态子类型必须事先通过已知类型机制声明,然后才能在序列化图中遇到该子类型的实例。

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

  • [KnownType(typeof(TDerivedObject))] 直接添加到静态声明的基类型中,如 Joost K.

    的答案所示

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

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    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。

  • 通过使用 DataContractJsonSerializerSettings 构造序列化程序,在运行时配置其他已知类型,其中已知类型在 DataContractJsonSerializerSettings.KnownTypes:

    中指定

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

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

    这里是演示小提琴#2。

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

  • 通过配置文件指定其他已知类型,如添加已知类型的其他方法中所示。

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

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

    1
    2
    3
    4
    5
    6
    [DataContract]
    //[KnownType(typeof(Sale))] Remove this
    public class Sale
    {
        // Remainder unchanged
    }

    有关更多信息,请参阅 Sowmy Srinivasan 的所有关于 KnownTypes。


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

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

    只有当字典中的所有项目都共享一个公共父对象时,这才有效,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
        [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  
        }

    然后使用字典作为

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