I'm trying to deserialize a pretty ugly JSON provided by an external REST API and am wondering about the "proper" way to do that (I'm using System.Text.Json in .net 6). Details follow:
I have a model for the data:
class DeviceData{
//lots of properties
}
which works fine (i.e I can just JsonSerializer.Deserialize<DeviceData> the response) when making an API query for a single instance, since it returns a nice JSON one would expect:
{
"property1_name": value,
"property2_name": value,
...
}
The problem begins when I use the batch query provided by the API, since the response to api_url/batch?=device1,device2,... looks as if someone failed to make an array (the device1s are alphanumeric strings pulled form a database) is:
{
"names":[
"device1",
"device2",
...
],
"device1":{
"stuff_i_dont_need": value,
"device1": {
"property1_name": value,
"property2_name": value,
...
}
}
"device2":{
...
}
...
}
The double nesting of dynamic property names means I can't just deserialize the second response as a dictionary of <string, myclass> pairs. I managed to hack something together using JsonDocument but it's extremly ugly and it feels like there should be a nice short way to do that with just JsonSerializer and maybe some reader overrides.
Using Deserialize subsections of a JSON payload from How to use a JSON document, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json as template you could do something like this:
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = new();
foreach(string name in root["names"]!.AsArray()) {
var o = root[name][name].AsObject();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
o.WriteTo(writer);
writer.Flush();
X? x = JsonSerializer.Deserialize<X>(stream.ToArray());
var innerJson = root[name][name].ToJsonString();
devices[name] = x;
}
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}");
This prints
device1: X { property1_name = 12, property2_name = 13 }
device2: X { property1_name = 22, property2_name = 23 }
I'm not sure if this is faster/better than calling ToJsonString():
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = new();
foreach(string name in root["names"]!.AsArray()) {
var innerJson = root[name][name].ToJsonString();
devices[name] = JsonSerializer.Deserialize<X>(innerJson);
}
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}")
If you're after fancy you could go full LINQ:
JsonNode root = JsonNode.Parse(json)!;
Dictionary<string, X> devices = root["names"]!.AsArray()
.Select(name => (string)name)
.ToDictionary(
keySelector: name => name,
elementSelector: name => System.Text.Json.JsonSerializer.Deserialize<X>(root[name][name].ToJsonString()));
foreach(var d in devices) Console.WriteLine($"{d.Key}: {d.Value}");
Both print
Related
I'm quite new to JSON, and am currently learning about (de)serialization.
I'm retrieving a JSON string from a webpage and trying to deserialize it into an object. Problem is, the root json key is static, but the underlying keys are dynamic and I cannot anticipate them to deserialize. Here is a mini example of the string :
{
"daily": {
"1337990400000": 443447,
"1338076800000": 444693,
"1338163200000": 452282,
"1338249600000": 462189,
"1338336000000": 466626
}
}
For another JSON string in my application, I was using a JavascriptSerializer and anticipating the keys using class structure. What's the best way to go about deserializing this string into an object?
Seriously, no need to go down the dynamic route; use
var deser = new JavaScriptSerializer()
.Deserialize<Dictionary<string, Dictionary<string, int>>>(val);
var justDaily = deser["daily"];
to get a dictionary, and then you can e.g.
foreach (string key in justDaily.Keys)
Console.WriteLine(key + ": " + justDaily[key]);
to get the keys present and the corresponding values.
You can use dynamic in .NET 4 or later. For example with JSON.NET I can do:
dynamic obj = JsonConvert.Deserialize<dynamic>("{x: 'hello'}");
You can then do:
var str = obj.x;
However, unsure how it will handle numeric keys. You can of course just use JObject directly itself, for example:
var obj = JObject.Parse("{'123456': 'help'}");
var str = obj["123456"];
Whenever you have JSON with dynamic keys it can usually be deserialized into a Dictionary<string, SomeObject>. Since the inner JSON keys are dynamic (in this question) the JSON can be modelled as:
Dictionary<string, Dictionary<string, int>>
I would recommend using NewtonSoft.Json (JSON.Net) or System.Text.Json (if you're working in .NET-Core 3.0 and up).
Newtonsoft.Json
Use DeserializeObject<T> from JsonConvert:
var response = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, int>>>(json);
System.Text.Json
Use Deserialize<T> from JsonSerializer:
var response = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, int>>>(json);
This is not convenient to use, because in с# can not be defined a variable starts with a number. Add prefix to keys.
Or try this:
string json = "
{ daily:[
{ key: '1337990400000', val:443447 },
{ key: '1338076800000', val:444693 },
{ key: '1338163200000', val:452282 },
{ key: '1338249600000', val:462189 },
{ key: '1338336000000', val:466626 }]
}";
public class itemClass
{
public string key; // or int
public int val;
}
public class items
{
public itemClass[] daily;
}
items daily = (new JavascriptSerializer()).Deserialize<items>(json);
Then you can:
var itemValue = items.Where(x=>x.key=='1338163200000').Select(x=>x.val).FirstOrDefault();
My JSON is a very long one and i am fetching only one section "parent_crumbs" from the long JSON
...................,
"parent_crumbs":["Platforms","New platform"],
"promise_by":"2016-08-01",
....
The code I used to fetch value of "parent_crumbs" is
JObject lp_p = JObject.Parse(response_json);
string val= lp_p["parent_crumbs"].ToString();
This returns the following value
"[\r\n \"Platforms\",\"New platform\"\r\n]"
Now I have to do a comparison with the first value from the array as the string is available in a Dictionary as key value and if available return ID
Packages = new Dictionary<string, int>();
Packages.Add("Platforms", 10212);
Packages.Add("New platform", 10202);
Packages.Add("Unknown platform", 10203);
int category=
if(Packages.ContainsKey(val))
{
Packages.TryGetValue(val, out category);
}
So with current code I can't do the comparison straight away due to presence of [\r\n etc.
How to get the value as a string Array without special chars like [\r\n .
Making Model Classes for the JSON for deserialization is not preferred way for me. Since creating class is a big job for me as lot of properties are there in JSON and is dynamic of nature
We can use the below code
var input = "[\r\n \"Platforms\",\"New platform\"\r\n]";
var array =(JArray) JsonConvert.DeserializeObject(input);
bool isEqual = array[0].Value<string>() == "Platforms";
you could also convert it to array with Linq
using System.Linq;
var tmp = lp_p["parent_crumbs"].Select(x => x.ToString());
foreach (var x in tmp)
{
Console.WriteLine(x.ToString());
}
By using Select, it will help you convert it to array rather than to string
You can use DeserializeAnonymousType method for that:
var myType = new
{
parent_crumbs = new []{ "" },
promise_by = default(DateTime)
};
var result = JsonConvert.DeserializeAnonymousType(json, myType);
int category = 0;
string key = result.parent_crumbs[0];
if(Packages.ContainsKey(key))
{
Packages.TryGetValue(key, out category);
}
References: DotNetFiddle Example, DeserializeAnonymousType
I'm quite new to JSON, and am currently learning about (de)serialization.
I'm retrieving a JSON string from a webpage and trying to deserialize it into an object. Problem is, the root json key is static, but the underlying keys are dynamic and I cannot anticipate them to deserialize. Here is a mini example of the string :
{
"daily": {
"1337990400000": 443447,
"1338076800000": 444693,
"1338163200000": 452282,
"1338249600000": 462189,
"1338336000000": 466626
}
}
For another JSON string in my application, I was using a JavascriptSerializer and anticipating the keys using class structure. What's the best way to go about deserializing this string into an object?
Seriously, no need to go down the dynamic route; use
var deser = new JavaScriptSerializer()
.Deserialize<Dictionary<string, Dictionary<string, int>>>(val);
var justDaily = deser["daily"];
to get a dictionary, and then you can e.g.
foreach (string key in justDaily.Keys)
Console.WriteLine(key + ": " + justDaily[key]);
to get the keys present and the corresponding values.
You can use dynamic in .NET 4 or later. For example with JSON.NET I can do:
dynamic obj = JsonConvert.Deserialize<dynamic>("{x: 'hello'}");
You can then do:
var str = obj.x;
However, unsure how it will handle numeric keys. You can of course just use JObject directly itself, for example:
var obj = JObject.Parse("{'123456': 'help'}");
var str = obj["123456"];
Whenever you have JSON with dynamic keys it can usually be deserialized into a Dictionary<string, SomeObject>. Since the inner JSON keys are dynamic (in this question) the JSON can be modelled as:
Dictionary<string, Dictionary<string, int>>
I would recommend using NewtonSoft.Json (JSON.Net) or System.Text.Json (if you're working in .NET-Core 3.0 and up).
Newtonsoft.Json
Use DeserializeObject<T> from JsonConvert:
var response = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, int>>>(json);
System.Text.Json
Use Deserialize<T> from JsonSerializer:
var response = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, int>>>(json);
This is not convenient to use, because in с# can not be defined a variable starts with a number. Add prefix to keys.
Or try this:
string json = "
{ daily:[
{ key: '1337990400000', val:443447 },
{ key: '1338076800000', val:444693 },
{ key: '1338163200000', val:452282 },
{ key: '1338249600000', val:462189 },
{ key: '1338336000000', val:466626 }]
}";
public class itemClass
{
public string key; // or int
public int val;
}
public class items
{
public itemClass[] daily;
}
items daily = (new JavascriptSerializer()).Deserialize<items>(json);
Then you can:
var itemValue = items.Where(x=>x.key=='1338163200000').Select(x=>x.val).FirstOrDefault();
I'm quite new to JSON, and am currently learning about (de)serialization.
I'm retrieving a JSON string from a webpage and trying to deserialize it into an object. Problem is, the root json key is static, but the underlying keys are dynamic and I cannot anticipate them to deserialize. Here is a mini example of the string :
{
"daily": {
"1337990400000": 443447,
"1338076800000": 444693,
"1338163200000": 452282,
"1338249600000": 462189,
"1338336000000": 466626
}
}
For another JSON string in my application, I was using a JavascriptSerializer and anticipating the keys using class structure. What's the best way to go about deserializing this string into an object?
Seriously, no need to go down the dynamic route; use
var deser = new JavaScriptSerializer()
.Deserialize<Dictionary<string, Dictionary<string, int>>>(val);
var justDaily = deser["daily"];
to get a dictionary, and then you can e.g.
foreach (string key in justDaily.Keys)
Console.WriteLine(key + ": " + justDaily[key]);
to get the keys present and the corresponding values.
You can use dynamic in .NET 4 or later. For example with JSON.NET I can do:
dynamic obj = JsonConvert.Deserialize<dynamic>("{x: 'hello'}");
You can then do:
var str = obj.x;
However, unsure how it will handle numeric keys. You can of course just use JObject directly itself, for example:
var obj = JObject.Parse("{'123456': 'help'}");
var str = obj["123456"];
Whenever you have JSON with dynamic keys it can usually be deserialized into a Dictionary<string, SomeObject>. Since the inner JSON keys are dynamic (in this question) the JSON can be modelled as:
Dictionary<string, Dictionary<string, int>>
I would recommend using NewtonSoft.Json (JSON.Net) or System.Text.Json (if you're working in .NET-Core 3.0 and up).
Newtonsoft.Json
Use DeserializeObject<T> from JsonConvert:
var response = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, int>>>(json);
System.Text.Json
Use Deserialize<T> from JsonSerializer:
var response = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, int>>>(json);
This is not convenient to use, because in с# can not be defined a variable starts with a number. Add prefix to keys.
Or try this:
string json = "
{ daily:[
{ key: '1337990400000', val:443447 },
{ key: '1338076800000', val:444693 },
{ key: '1338163200000', val:452282 },
{ key: '1338249600000', val:462189 },
{ key: '1338336000000', val:466626 }]
}";
public class itemClass
{
public string key; // or int
public int val;
}
public class items
{
public itemClass[] daily;
}
items daily = (new JavascriptSerializer()).Deserialize<items>(json);
Then you can:
var itemValue = items.Where(x=>x.key=='1338163200000').Select(x=>x.val).FirstOrDefault();
I need to cast JArray which I received from browser to list of MyType object. The problem is that I don't want to use .ToObject<> extension because object sended from browser can have missing or extra values and I cannot change deserializer. I wrote my own parser to receive data from single JObject. Usage:
foreach (var _object in _jarray)
{
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
}
What I want to do is something like this
_jarray.ForEach(_object => {
using (var tp = new TokenParser(_object))
{
MyType test = new MyType()
{
id = tp.ConvertToInt("token_key")
};
}
});
I don't want to cast elements like:
_jarray.Select(x =>
(int)["token_key"]
);
I tried to wrote this extension based on LINQ equivalent of foreach for IEnumerable<T> but it seems that Action requires T as parameter type. I don't need this because JArray is an array of JObject.
Is this possible to achieve?