【问题标题】:How can I select an object type to deserialize using Json.Net based on property values in the data如何根据数据中的属性值使用 Json.Net 选择要反序列化的对象类型
【发布时间】:2014-10-28 20:49:22
【问题描述】:

我需要像这样反序列化 json(它是在 DICOM 中定义的 - 一个国际标准,所以我不能改变格式!)

{
....
"00080060": {
  "Value": [
    "US"
  ],
  "vr": "CS"
},
"00080070": {
  "Value": [
    "ACME Products"
  ],
  "vr": "LO"
},
"00080090": {
  "Value": [
    {
      "AlphabeticName": "Better^Make^U.^MD"
    }
  ],
  "vr": "PN"
},
"00081110": {
  "Value": [
    {
      "00080008": {
        "Value": [
          "XX_0",
          "OIU",
          null,
          "PPP"
        ],
        "vr": "CS"
      }
    },
    {},
    {
      "00080008": {
        "Value": [
          "XX_2",
          "OIU",
          null,
          "PPP"
        ],
        "vr": "CS"
      }
    }
  ],
  "vr": "SQ"
},

每个属性(在一个长列表中!)都有一个名称(在上面的示例中为 0008XXXX),并具有子属性“vr”和 Value。在大多数情况下,Value 只是一个对象数组(字符串或数字),这很好,但对于 2 种特殊情况(如上的 PN 和 SQ),它需要特殊处理,而在 SQ 的情况下,它实际上是一个再次是顶级对象(可以无限递归地嵌套......)

所以 - 我需要一个简单的方法,在反序列化过程中检查“vr”值并为 json.net 提供它应该用于相关“值”的类型 - 有没有简单的方法可以做到这一点?当然,vr 可能出现在 Value 之前或之后(取决于远程实现)这一事实可能会使事情变得更加复杂......

我查看了 json.net 的 ContractResolver 和 JsonConverter 机制,但 ContractResolver 似乎不允许我访问正在读取的数据以允许做出选择,而 JsonConverter 派生类似乎让我不得不重新- 对我来说已经实现了 json.net 的大部分内容(否则太好了!):-(

我是否在这里遗漏了一些明显而简单的解决方案?

【问题讨论】:

  • JObject 是您所需要的。不幸的是,我没有足够的时间来使用它进行解析。

标签: c# serialization recursion json.net json-deserialization


【解决方案1】:

呃,这是一种难以处理的格式。但是,应该可以使用自定义 JsonConverter 来理解它。在转换器内部使用JObject 将使我们免受大部分繁重的工作。但首先,我们需要定义几个类来反序列化数据。

第一堂课我会打电话给Node;这将包含Value 列表和vr

class Node
{
    public IList Value { get; set; }
    public string vr { get; set; }
}

第二类是用来存放“PN”箱的物品的。

class PnItem
{
    public string AlphabeticName { get; set; }
}

这是转换器的代码。转换器可以查看vr 属性并使用该信息为Value 创建正确类型的列表。

class NodeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Node));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        Node node = new Node();
        node.vr = (string)jo["vr"];
        if (node.vr == "PN")
        {
            node.Value = jo["Value"].ToObject<List<PnItem>>(serializer);
        }
        else if (node.vr == "SQ")
        {
            node.Value = jo["Value"].ToObject<List<Dictionary<string, Node>>>(serializer);
        }
        else
        {
            node.Value = jo["Value"].ToObject<List<string>>(serializer);
        }
        return node;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

请注意,对于“SQ”案例,我们正在反序列化 List&lt;Dictionary&lt;string, Node&gt;&gt;。这涵盖了递归结构。每当 Json.Net 尝试反序列化一个节点时,它都会回调到转换器。我们使用Dictionary 来处理属性名称可以变化的事实(例如“00080070”、“00080090”等)。出于同样的原因,我们还必须在根目录上反序列化为 Dictionary&lt;string, Node&gt;

因此,为了将所有内容联系在一起,您可以通过以下方式反序列化您的 JSON:

var dict = JsonConvert.DeserializeObject<Dictionary<string, Node>>(json, 
                                                            new NodeConverter());

这是一个演示:https://dotnetfiddle.net/hsFlxU

【讨论】:

  • 绝对精彩——正是我所需要的!我不得不进行一些修改以适应我为清楚起见而遗漏的其他一些奇怪的东西(例如,整个东西是一个 json 数组),所以我们最终也使用相同的双层泛型作为顶层
  • 我感觉可能还有一些细节没有说出来!很高兴您能够调整此解决方案以满足您的需求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
相关资源
最近更新 更多