Reputation: 840
I've a valid JSON object that has a JSON array in it. The JSON array doesn't have curly braces and contains a comma separated list of mixed type in it. It looks like this:
{
"ID": 17,
"Days": 979,
"Start_Date": "10/13/2012",
"End_Date": "11/12/2012",
"State": "",
"Page": 1,
"Test": "Valid",
"ROWS": [
[441210, "A", "12/31/2009", "OK", "Done", "KELLEY and Co'", "12/31/2009", "06/29/2010", "TEXAS", "Lawyers", 6, "", "<img src=\"/includes/images/Icon_done.gif\" border=\"0\" alt=\"Done\" title=\"Done\" />"],
[441151, "B", "12/31/2009", "No", "In-process", "Sage & Co", "12/31/2009", "06/29/2010", "CALIFORNIA", "Realtor", 6, "", "<img src=\"/includes/images/Icon_InProcess.gif\" border=\"0\" alt=\"In Process\" title=\"In Process\" />"]
]
}
I've created a class to reflect the JSON structure, having a List for the complex array:
class myModel
{
public int ID { get; set; }
public int Days { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string State { get; set; }
public string Page { get; set; }
public string Test { get; set; }
List<ChildModel> Rows { get; set; }
}
I've tested it with a List of a List too:
List<List<ChildModel>> Rows { get; set; }
And the child model like this:
class ChildModel
{
public int ID { get; set; }
public string StatusId { get; set; }
public DateTime ContactDate { get; set; }
public string State { get; set; }
public string Status { get; set; }
public string CustomerName { get; set; }
public DateTime WorkStartDate { get; set; }
public DateTime WorkEndDate { get; set; }
public string Territory { get; set; }
public string CustType { get; set; }
public int JobOrder { get; set; }
public string Filler { get; set; }
public string Link { get; set; }
}
In my program.cs file, I'm deserializing it like this:
using (StreamReader r = new StreamReader(@"D:\01.json"))
{
myModel items = JsonConvert.DeserializeObject<myModel>(r.ReadToEnd());
}
When I run this program, the child object (Rows) is always null
. What am I doing wrong?
Upvotes: 5
Views: 2670
Reputation: 129807
Json.Net does not have a facility to automatically map an array into a class. To do so you need a custom JsonConverter
. Here is a generic converter that should work for you. It uses a custom [JsonArrayIndex]
attribute to identify which properties in the class correspond to which indexes in the array. This will allow you to easily update your model if the JSON changes. Also, you can safely omit properties from your class that you don't need, such as Filler
.
Here is the code:
public class JsonArrayIndexAttribute : Attribute
{
public int Index { get; private set; }
public JsonArrayIndexAttribute(int index)
{
Index = index;
}
}
public class ArrayToObjectConverter<T> : JsonConverter where T : class, new()
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
var propsByIndex = typeof(T).GetProperties()
.Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<JsonArrayIndexAttribute>() != null)
.ToDictionary(p => p.GetCustomAttribute<JsonArrayIndexAttribute>().Index);
JObject obj = new JObject(array
.Select((jt, i) =>
{
PropertyInfo prop;
return propsByIndex.TryGetValue(i, out prop) ? new JProperty(prop.Name, jt) : null;
})
.Where(jp => jp != null)
);
T target = new T();
serializer.Populate(obj.CreateReader(), target);
return target;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, you need to mark up your ChildModel
class as shown below:
[JsonConverter(typeof(ArrayToObjectConverter<ChildModel>))]
class ChildModel
{
[JsonArrayIndex(0)]
public int ID { get; set; }
[JsonArrayIndex(1)]
public string StatusId { get; set; }
[JsonArrayIndex(2)]
public DateTime ContactDate { get; set; }
[JsonArrayIndex(3)]
public string State { get; set; }
[JsonArrayIndex(4)]
public string Status { get; set; }
[JsonArrayIndex(5)]
public string CustomerName { get; set; }
[JsonArrayIndex(6)]
public DateTime WorkStartDate { get; set; }
[JsonArrayIndex(7)]
public DateTime WorkEndDate { get; set; }
[JsonArrayIndex(8)]
public string Territory { get; set; }
[JsonArrayIndex(9)]
public string CustType { get; set; }
[JsonArrayIndex(10)]
public int JobOrder { get; set; }
[JsonArrayIndex(12)]
public string Link { get; set; }
}
Then just deserialize as usual and it should work as you wanted. Here is a demo: https://dotnetfiddle.net/n3oE3L
Note: I did not implement WriteJson
, so if you serialize your model back to JSON, it will not serialize back to the array format; instead it will use the default object serialization.
Upvotes: 9
Reputation: 2123
Use this to deserialize your data into a json object...
public class Rootobject
{
public int ID { get; set; }
public int Days { get; set; }
public string Start_Date { get; set; }
public string End_Date { get; set; }
public string State { get; set; }
public int Page { get; set; }
public string Test { get; set; }
public object[][] ROWS { get; set; }
}
...and then create a utility function that converts the object[][]
to your desired target object...
(created with visual studio: Edit -> Paste Special -> JSON classes)
Upvotes: 2