The JToken object in the Json.net library is very useful, you can access json objects and arrays without needing a complex set of domain model classes. I use it when I have to access a few isolated properties of a complex object graph. I don't want to define a whole load of boilerplate classes just so I can access a couple of properties.
The JToken type is also very useful when you don't know how or when the json might change. Keeping a class hierarchy up to date can be very painful.
There's a good example of the usefulness of JToken here:
https://www.newtonsoft.com/json/help/html/QueryJsonSelectToken.htm
You may already have classes that you use to deserialize json, you can still use JToken as properties where you expect the json to change.
Using JTokens for the Data object list would work.
For your case:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class demo
{
[JsonProperty(PropertyName = "a1")]
public int? a1 { get; set; }
[JsonProperty(PropertyName = "a2")]
public int? a2 { get; set; }
[JsonProperty(PropertyName = "a3")]
public int? a3 { get; set; }
[JsonProperty(PropertyName = "data")]
public List<JToken> data { get; set; }
}
var json = @"{
""a1"": 10,
""a2"": 11,
""a3"": 13,
""uuid"": ""1c18f0c8-02d0-425a-8dc7-13dc6d0b46af"",
""data"": [
{
""id"": 1,
""timeStamp"": ""2018-01-03T08:01:00Z"",
""quantity"": 200.0,
""tag"": ""/sometag/""
},
{
""id"": 2,
""timeStamp"": ""2018-01-03T08:05:00Z"",
""quantity"": 100.0,
""tag"": ""/someothertag/""
},
{
""id"": 3,
""name"": ""somename"",
""age"": 32
}
]
}";
var demo1 = JsonConvert.DeserializeObject<demo>(json);
// Let's get the timeStamp of item 2.
var timeStamp = demo1.data[1].SelectToken("timeStamp").ToObject<DateTime>();
// Let's get the age of item 3.
var age = demo1.data[2].SelectToken("age").ToObject<int>();
You can also make use of JToken.Parse for the whole lot, e.g.
var obj1 = JToken.Parse(json);
// Get a1
var a1 = obj1.SelectToken("a1", false).ToObject<int>();
// Get uuid
var uuid = obj1.SelectToken("uuid", false).ToObject<Guid>();
// Let's get the timeStamp of item 2.
var timeStamp = obj1.SelectToken("data[1].timeStamp").ToObject<DateTime>();
// Let's get the age of item 3.
var age = obj1.SelectToken("data[2].age").ToObject<int>();
When calling the SelectToken method, you can pass the parameter errorWhenNoMatch to indicate whether an error should be thrown when the property/value does not exist. This allows you to write some very robust code when the json data is of an unknown structure.
If you want to enumerate/traverse properties you can do something like this:
static void TraverseProperties(JToken jtoken)
{
foreach (var value in jtoken.Values())
{
if (!value.HasValues)
{
Console.WriteLine(value.Path + ": " + value.ToObject<string>());
}
else
{
TraverseProperties(value);
}
}
}
TraverseProperties(obj1);