c# - 无法使用 Json.net 序列化具有复杂键的字典

我有一个自定义 .net 类型作为其键的字典。我正在尝试使用 JSON.net 将此字典序列化为 JSON,但是它无法在序列化过程中将键转换为正确的值。

class ListBaseClass
{
    public String testA;
    public String testB;
}
-----
var details = new Dictionary<ListBaseClass, string>();
details.Add(new ListBaseClass { testA = "Hello", testB = "World" }, "Normal");
var results = Newtonsoft.Json.JsonConvert.SerializeObject(details);
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<ListBaseClass, string>> results);

这给我 --> "{\"JSonSerialization.ListBaseClass\":\"Normal\"}"

但是,如果我将自定义类型作为 Dictionary 中的值,则效果很好

  var details = new Dictionary<string, ListBaseClass>();
  details.Add("Normal", new ListBaseClass { testA = "Hello", testB = "World" });
  var results = Newtonsoft.Json.JsonConvert.SerializeObject(details);
  var data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, ListBaseClass>>(results);

这给我 --> "{\"Normal\":{\"testA\":\"Hello\",\"testB\":\"World\"}}"

如果我遇到了 Json.net 的一些限制或者我做错了什么,有人可以建议吗?

最佳答案

您可能不想使用 Gordon Bean 提出的答案。该解决方案有效,但它提供了用于输出的序列化字符串。如果您使用的是 JSON,这将给您带来不太理想的结果,因为您确实想要对象的 JSON 表示,而不是字符串表示。

例如,假设您有一个将唯一网格点与字符串相关联的数据结构:

class Point
{
    public int x { get; set; }
    public int y { get; set; }
}

public Dictionary<Point,string> Locations { get; set; };

使用 TypeConverter 覆盖,当您序列化它时,您将获得该对象的字符串表示形式。

"Locations": {
  "4,3": "foo",
  "3,4": "bar"
},

但我们真正想要的是:

"Locations": {
  { "x": 4, "y": 3 }: "foo",
  { "x": 3, "y": 4 }: "bar"
},

重写 TypeConverter 来序列化/反序列化类有几个问题。

首先,这不是 JSON,您可能需要编写额外的自定义逻辑来处理其他地方的序列化和反序列化。 (例如,可能是您的客户端层中的 Javascript?)

其次,使用此对象的任何其他地方现在都会喷出此字符串,之前它已正确序列化为对象:

"GridCenterPoint": { "x": 0, "y": 0 },

现在序列化为:

"GridCenterPoint": "0,0",

你可以稍微控制一下 TypeConverter 的格式,但你无法摆脱它被呈现为字符串而不是对象的事实。

这个问题不是序列化程序的问题,因为 Json.NET 会仔细检查复杂的对象而不会错过任何一个节拍,这是处理字典键的方式的问题。如果您尝试获取示例对象,并序列化 List 甚至 Hashset,您会注意到生成正确的 JSON 没有问题。这为我们提供了一种更简单的方法来解决这个问题。

理想情况下,我们只想告诉 Json.NET 将键序列化为任何对象类型,而不是将其强制为字符串。由于这似乎不是一个选项,另一种方法是给 Json.NET 一些它可以使用的东西:a List<KeyValuePair<T,K>> .

如果您将 KeyValuePairs 列表输入 Json.NET 的序列化程序,您将得到您所期望的。例如,这是一个您可以实现的更简单的包装器:

    private Dictionary<Point, string> _Locations;
    public List<KeyValuePair<Point, string>> SerializedLocations
    {
        get { return _Locations.ToList(); }
        set { _Locations= value.ToDictionary(x => x.Key, x => x.Value); }
    }

这个技巧很有效,因为 kvp 中的键不会被强制转换为字符串格式。你问为什么是字符串格式?它打败了我。 Dictionary 对象实现了IEnumerable<KeyValuePair<TKey, TValue>>接口(interface),因此以与 kvps 列表相同的方式对其进行序列化应该没有任何问题,因为这本质上就是字典。有人(詹姆斯牛顿?)在编写 Newtonsoft 字典序列化程序时做出了一个决定,即复杂的键太乱而无法处理。可能有一些我没有考虑过的极端情况使这个问题变得更加棘手。

这是一个更好的解决方案,因为它生成实际的 JSON 对象,技术上更简单,并且不会产生因替换序列化程序而导致的任何副作用。

https://stackoverflow.com/questions/24504245/

相关文章:

javascript - Web Workers - JSON 的可传输对象

java - jackson :反序列化为每个值都具有正确类型的 Map

php - 为什么在我们有 json_encode 时使用 CJSON 编码

java - JSONObject.toString : how NOT to escape sla

ruby-on-rails - Rails 4 为 API 操作跳过protect_from_for

c# - 将带有数据和文件的 JSON 发布到 Web Api - jQuery/MVC

c# - JSON.NET JObject 键比较不区分大小写

javascript - 我可以使用空字符串作为对象标识符吗?

json - 通过身份引用对象的标准方式(例如,循环引用)?

asp.net - jqgrid 与 asp.net webmethod 和 json 一起使用排序