Reputation: 33
I'm currently dealing with getting data from an external API. The data I receive looks something like what is shown below. (Just a mockup; don't expect the values to make any sense. It's just to illustrate what kind of data I get.)
{
"user": [
{
"key": "12345678",
"data": [
{
"id": "Name",
"string": "Bob"
},
{
"id": "ElapsedTimeSinceLastMessage",
"timestamp": 1618233964000
},
{
"id": "Age",
"number": 27
}
]
}
]
}
I don't really know how I should be going about deserializing this JSON.
The classes I'm using to deserialize right now look like this:
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<DataEntry> DataEntries { get; set; }
}
public class DataEntry
{
[JsonProperty("id")]
public string Id { get; set; }
public Type Value { get; set; }
}
And I don't know what I need to set in order to deserialize the Value
inside the DataEntry
. Maybe someone can guide me into the right direction?
Upvotes: 3
Views: 2339
Reputation: 129697
The Data
part of this JSON is really just a Dictionary<string, object>
in disguise. You can use a custom JsonConverter
to transform the list of id/value pairs into that format for easy use.
Frist, define these classes:
class RootObject
{
[JsonProperty("user")]
public List<User> Users { get; set; }
}
class User
{
public string Key { get; set; }
[JsonConverter(typeof(CustomDataConverter))]
public Dictionary<string, object> Data { get; set; }
}
Next, define the converter:
class CustomDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Dictionary<string, object>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JToken.Load(reader)
.Children<JObject>()
.ToDictionary(jo => (string)jo["id"],
jo => jo.Properties()
.Where(jp => jp.Name != "id" && jp.Value is JValue)
.Select(jp => ((JValue)jp.Value).Value)
.FirstOrDefault());
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can then deserialize and dump out the data like this:
var root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (User user in root.Users)
{
Console.WriteLine("User Key: " + user.Key);
foreach (var kvp in user.Data)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
}
Here is a working demo: https://dotnetfiddle.net/GIT4dl
Upvotes: 1
Reputation: 34997
One angle of attack would be with Dictionaries:
public class WithUser
{
public List<User> User { get; set; }
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<Dictionary<string,object>> DataEntries { get; set; }
}
The extraction is a bit of a pain but possible:
public static void Main()
{
var json = File.ReadAllText("Example.json");
var x = JsonConvert.DeserializeObject<WithUser>(json);
var user = x.User.Single();
var age = Extract<long>(user, "Age");
var name = Extract<string>(user, "Name");
var elapsedTimeSinceLastMessage = TimeSpan.FromTicks(Extract<long>(user, "ElapsedTimeSinceLastMessage"));
}
public static T Extract<T>(User user, string name)
{
var o = user.DataEntries
.SingleOrDefault(d => (string)d["id"] == name) // Find the one with age
.SingleOrDefault(kvp => kvp.Key != "id") // Find the not 'id' value
.Value; // Take the value
return (T)o;
}
Upvotes: 1
Reputation: 376
You can use Json to C#. It generates following classes from your json string. As you can see, you can also use nullable types(long?
, int?
). If there is a value, sets the required variable. Otherwise leaves it as null. In this way, you can get your different type according to id of data.
public class DataEntry
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("string")]
public string String { get; set; }
[JsonProperty("timestamp")]
public long? Timestamp { get; set; }
[JsonProperty("number")]
public int? Number { get; set; }
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<DataEntry> Data { get; set; }
}
public class Root
{
public List<User> User { get; set; }
}
To Deserialize:
string response = "{\"user\":[{\"key\":\"12345678\",\"data\":[{\"id\":\"Name\",\"string\":\"Bob\"},{\"id\":\"ElapsedTimeSinceLastMessage\",\"timestamp\":1618233964000},{\"id\":\"Age\",\"number\":27}]}]}";
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(response);
Upvotes: 0
Reputation: 205
The main issue is in the models that you created.
First of all, base on the JSON, you need another class that contains a list of Users.
public class ResultClass
{
public List<User> User { get; set; }
}
After that, because the second object of data property has not a constant name, we can't specify a constant name for it (like value). We should define the data property as an object. So the user class should be like this:
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<object> DataEntries { get; set; }
}
In the end, in the controller, you should deserialize the ResultJson class:
var result = JsonConvert.DeserializeObject<ResultClass>(jsonTxt);
Upvotes: 0