How to choose object type for deserialization using Json.Net based on property values ​​in data

I need to deserialize json like this (it is defined in DICOM - the international standard, so I cannot change the format!)

{ .... "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" }, 

Each property (in a long list!) Has a name (0008XXXX in the example above) and has sub-properties "vr" and a value. In most cases, Value is just an array of objects (a string or a number) and this is fine, but for 2 special cases (PN and SQ, as indicated above) it needs special processing, and in the case of SQ it is actually an array from a top-level object again ( which can be nested recursively ad infinitum ...)

So, I need a simple method during deserialization to check the value of β€œvr” and give json.net the type that it should use for the associated β€œValue” - is there a simple way to do this? Of course, the fact that vr may appear before or after Value (depending on the remote implementation) can make things even more complicated ...

I looked at the json.net ContractResolver and JsonConverter mechanisms, but the ContractResolver does not seem to give me access to the data being read to allow a choice, and the JsonConverter derived classes seem to use most of what json.net is for dong (otherwise good!) for me already: - (

Am I missing some obvious and simple solution here?

+5
source share
1 answer

Wow, this is a complicated format to work with. However, this should be possible to understand using a custom JsonConverter . Using JObject inside the converter will protect us from most of the heavy lifting. But first, we need to define a couple of classes to deserialize the data.

The first class I'll call Node ; it will contain a list of Value and vr .

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

The second class is necessary for storing elements for the case of "PN".

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

Here is the code for the converter. The converter can look at the vr property and use this information to create the correct list type for 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(); } } 

Note that for the "SQ" case, we deserialize a List<Dictionary<string, Node>> . This spans a recursive structure. Whenever Json.Net tries to deserialize Node, it will call back to the converter. We use Dictionary to handle the fact that property names may vary (for example, "00080070", "00080090", etc.). In the root, we must also deserialize to Dictionary<string, Node> for the same reason.

So, to bundle it all together, here is how you would deserialize your JSON:

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

Here is a demo: https://dotnetfiddle.net/hsFlxU

+5
source

Source: https://habr.com/ru/post/1201795/


All Articles