问题
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