Json.net deserialising to objects from JSon that may contain either an array, or a nested array

泄露秘密 提交于 2019-12-24 00:23:50

问题


I have a number of JSon files that I'm deserialising using

JsonSerializer serializer = new JsonSerializer(); t obj = (t)serializer.Deserialize(file, typeof(t));

to a collection of objects. They contain, amongst other things the following data. The number of arrays in "truthtable" is determined by the value of "gates"

"gates" : 1,
"truthtable" : [ false, true ]

and

"gates" : 2,
"truthtable" : [ [ false, false ], [ false, true ] ]

if I try to deserialise "truthtable" to the following property, example 1 fails.

public List<List<bool>>truthtable { get; set; }

Is there any way I can deserialise these two different types of truthtable to the same object? I've tried building a custom deserialiser, but Json sees both as "JsonToken.StartArray", so can't differentiate that way.

Ideally, I'd like to be able to deserialise both examples as if they were arrays of arrays of booleans.

Edit Should have mentioned, I cannot alter the way the Json files are created. I don't have access to their creation.


回答1:


This problem can be solved using a custom JsonConverter. The converter can read the number of gates and then populate the List<List<bool>> accordingly. If there is only one gate, it can wrap the single list in an outer list to make it work with your class.

Assuming the class that you are trying to deserialize into looks something like this:

class Chip
{
    public int Gates { get; set; }
    public List<List<bool>> TruthTable { get; set; }
}

then the converter might look something like this:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);

        Chip chip = new Chip();
        chip.Gates = (int)jo["gates"];
        JArray ja = (JArray)jo["truthtable"];

        if (chip.Gates == 1)
        {
            chip.TruthTable = new List<List<bool>>();
            chip.TruthTable.Add(ja.ToObject<List<bool>>());
        }
        else
        {
            chip.TruthTable = ja.ToObject<List<List<bool>>>();
        }

        return chip;
    }

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

To use the converter, create an instance and add it to the serializer's Converters collection before you deserialize:

serializer.Converters.Add(new ChipConverter());

Or if you prefer, you can annotate your class with a [JsonConverter] attribute instead:

[JsonConverter(typeof(ChipConverter))]
class Chip
{
    ...
}

Here's a demo showing the converter in action (note I used JsonConvert.DeserializeObject<T>() here instead of creating a JsonSerializer instance, but it works the same way):

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
            {
                ""gates"": 1,
                ""truthtable"": [ false, true ]
            },
            {
                ""gates"": 2,
                ""truthtable"": [ [ false, false ], [ false, true ] ]
            }
        ]";

        List<Chip> chips = JsonConvert.DeserializeObject<List<Chip>>(json,
                                                         new ChipConverter());

        foreach (Chip c in chips)
        {
            Console.WriteLine("gates: " + c.Gates);
            foreach (List<bool> list in c.TruthTable)
            {
                Console.WriteLine(string.Join(", ",
                    list.Select(b => b.ToString()).ToArray()));
            }
            Console.WriteLine();
        }
    }
}

Output:

gates: 1
False, True

gates: 2
False, False
False, True



回答2:


Simple... change the method who generates truthtable (in case of gate == 1) to output array of array (or List>).

"gates" : 1, "truthtable" : [[ false,true ]]

Keep in mind, you have a type in your data contract,in this case the data contract expects an List> and you sent a wrong type (List<>). Can you wrote the code who generated "truthtable" in gate == 1 case?

PS. Sorry,i don´t speak english very well... =)




回答3:


Assuming you're using .NET 4.0 or above, you can deserialize the json into a Dynamic Object

string jsonData = ....; // fill your json data here
dynamic d = JsonConvert.DeserializeObject(jsonData);

then check the type of d.truthtable[0] and determine what to do when d.truthtable is an array or a nested array

if (d.truthtable != null && d.truthtable.Count > 0)
{
    if (d.truthtable[0].GetType() == typeof(Newtonsoft.Json.Linq.JValue))
    {
        // do something when truthtable is an array
    }
    else if (d.truthtable[0].GetType() == typeof(Newtonsoft.Json.Linq.JArray))
    {
        // do something when truthtable is a nested array
    }
}



回答4:


I´m assuming, this is a data contract conflict, and i´m suggest a workarrond... only think in performance...

In that case, is not elegant but, you can change the type in a easy way and force truthtable to be Array>..

String json = ...

        int idx = 0;
        while (idx >-1)
        {
            idx = json.Trim().IndexOf("\"truthtable\":[",idx);
            if (idx >-1 && json[idx + 15] != '[')
            {
                idx += 14;
                json = json.Insert(idx, "[");
                json = json.Insert(json.IndexOf("]", idx),"]");
            }
        }

This help you??



来源:https://stackoverflow.com/questions/21940989/json-net-deserialising-to-objects-from-json-that-may-contain-either-an-array-or

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!