C# Iterate over a JSON object - c#

I'm trying to use the .Net Json deserializer System.Text.Json to iterate over the following Json input.
I've found methods for iterating by "person_id" if it were the top level in the json structure (see below), but I haven't been able to track down a method for looping through all of the next level items.
I think the NewtonSoft JArray and JObject are pretty close to what I need (I may pursue that) but wasn't sure if Microsofts solution had a way to do it...
Is this possible with Microsoft's System.Text.Json library?
{
"0": {
"person_id": "0",
"last_name": "Greg",
"first_name": "Craig",
},
"1": {
"person_id": "1",
"last_name": "Doe",
"first_name": "John",
}
}
JsonElement solution for extracting an object by property name (ie I can get John Doe's information this way).
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("1");
}

Looks similar to some JSON I've had to tackle before. It helps to remember that JSON objects are just key/value pairs.
With this in mind, the data structure you have could be interpreted as a Dictionary in C#.
I've used Newtonsoft.Json in my examples, but they are easy to swap over to use System.Text.Json.
In its simplest form, you could use:
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString);
Which will give you a Dictionary where the key is the "0", "1" properties, and the value is the object representing the person info inside.
You can then run a simple foreach loop over the Dictionary, or its keys, or values, like so:
foreach (var item in dictionary)
{
var key = item.Key;
var person = item.Value;
}
But the person (item.Value) is still only of type object.
This is where you can create a class to represent each Person's info, decorating the properties to match what's in the JSON data structure, like so:
public class Person
{
[JsonProperty("person_id")]
public string PersonId { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
and instead deserialize your JSON to a Dictionary<string, Person>:
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, Person>>(jsonString);
Which means each value of the entry in the dictionary is an instance of Person, and you can access the strongly-typed properties of each, like so:
foreach (var item in dictionary)
{
var key = item.Key; // although you probably won't need this
Console.WriteLine(key);
var person = item.Value;
Console.WriteLine(person.PersonId);
Console.WriteLine(person.FirstName);
Console.WriteLine(person.LastName);
}
// which gives the following output:
> 0
> 0
> Craig
> Greg
> 1
> 1
> John
> Doe
Although I've used Newtonsoft.Json above, it's easy to "migrate" the above sample to use System.Text.Json.
Instead of JsonConvert.DeserializeObject<T>, use JsonSerializer.Deserialize<T>; and
Instead of using [JsonProperty("property_name")] attribute, you can use [JsonPropertyName("property_name")].
Hope this helps.

You can use JsonElement.EnumerateObject. It is used to enumerate the properties in the JSON object represented by this JsonElement
var options = new JsonDocumentOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
};
using (JsonDocument document = JsonDocument.Parse(jsos, options))
{
foreach (JsonProperty property in document.RootElement.EnumerateObject())
{
string key = property.Name;
JsonElement valueElement = property.Value;
string person_id = valueElement.GetProperty("person_id").GetString();
string last_name = valueElement.GetProperty("last_name").GetString();
string first_name = valueElement.GetProperty("first_name").GetString();
}
}

Related

JSON to datatable - How to deserialize

I have this very simple JSON string:
{
"data": {
"id": 33306,
"sport": {
"id1": "FB",
"id2": "HB"
}
}
}
I can't understand how to return a datatable from this string.
I have tried to use this code but it's not working:
DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
you have to flatten all json properties, after this to convert to DataTable
var jObj = (JObject)JObject.Parse(json)["data"];
var properties = jObj.Properties().ToList();
for (var i = 0; i < properties.Count; i++)
{
var prop = properties[i];
if (prop.Value.Type == JTokenType.Object)
{
foreach (var p in ((JObject)prop.Value).Properties())
jObj.Add(new JProperty(prop.Name + " " + p.Name, p.Value));
prop.Remove();
}
}
DataTable dt = new JArray { jObj }.ToObject<DataTable>();
output
[
{
"id": 33306,
"sport id1": "FB",
"sport id2": "HB"
}
]
You need to do this in two steps.
Deserialize into a .net object whose structure matches that of the JSON
Populate a DataTable with the properties of that object
Step 1 - Deserialize
We need to define some classes to receive the deserialized data. The names of the classes aren't particularly important (as long as they're meaningful to you), however the names of the properties of those classes need to match the names of the corresponding elements in the JSON.
First the outermost class, which is the shape of the JSON you want to deserialize.
public class SomeClass
{
public DataElement Data { get; set; }
}
Now we need to define the shape of DataElement.
public class DataElement
{
public int Id { get; set; }
public SportElement Sport { get; set; }
}
And now we need to define the shape of SportElement.
public class SportElement
{
public string Id1 { get; set; }
public string Id2 { get; set; }
}
The implementation above is fairly rigid and assumes that the shape of the JSON doesn't change from one document to the next. If, however, you expect the shape to vary, for example, if the sport element could could contain any number of id1, id2, id3, ... id100 etc elements, then you can throw away the SportElement class and use a dictionary to represent that element instead.
public class DataElement
{
public int Id { get; set; }
public Dictionary<string, string> Sport { get; set; }
}
Which of those two approaches to use will depend on how predictable the structure of the JSON is (or whether or not it's under your control). I find that using a dictionary is a good way of coping with JSON produced by 3rd party applications which aren't under my control, but the resulting objects aren't as easy to work with as those where I know exactly what shape the JSON will always be and can create a strongly-typed class structure representing that shape.
Whichever approach you choose, the usage is the same:
var deserialized = JsonConvert.DeserializeObject<SomeClass>(json);
Step 2 - Populate the DataTable from the object
How to do this step will depend on what you want the DataTable to look like (which is not clear from the question). For example, you might want it to look like this (which I think is what Serge's answer would return).
id
sport id1
sport id2
33306
FB
HB
Or (if for example the sport element could contain any number of id1, id2 and so on elements) you might want it to look like this.
id
sport
33306
id1: FB
33306
id2: HB
Or you might want some different representation altogether. Sorry if that's an incomplete answer, if you want to update the question with what you'd expect the DataTable to look like then I can update this answer with more detail on how to go about step 2.

Deserialize a generic JSON and use its properties

I need to call an API that returns a JSON that can vary.
Sometimes the response could be something like:
[
{
"name": "value"
}
]
while other times
[
{
"property": "value"
}
]
or even
[
{
"name": "value",
"status": "value",
"count": "value"
}
]
Basically, I need to retrieve all values in this JSON for later use.
I tried
var prova = JsonConvert.DeserializeObject<dynamic>(result);, but I can't access the properties using ["indexName"] because the index can vary depending on some factors. Any ideas?
You can create a POCO that that has all the possible properties if they are finite. Like the following :
public class Result {
public string name { get; set; }
public string property { get; set; }
public string status { get; set; }
public string count{ get; set; }
}
And then you can use: var prova = JsonConvert.DeserializeObject<IEnumerable<Result>>(Result);. I change the deserialization to using an IEnumerable because I see that your JSON is an array.
And then you can check if the values are null before accessing them. Since every Result object will only have the property that was available in JSON as non-null.
Alternatively, you can use a Dictionary<string, string> or Dictionary<string, int> and do the following :
var prova = JsonSerializer.Deserialize<IEnumerable<Dictionary<string, string>>>(result) as List<Dictionary<string, string>>;
and then you can access every object of the list and access the property you want like this or TryGetValue Method for more safety.
Console.WriteLine(prova[0]["name"]);
Assumption:
You're getting an array of objects from the api (that doesn't return null data - re: probably why the structure is "dynamic").
Here's one way:
//System.Text.Json
var foo = JsonSerializer.Deserialize<JsonElement>(array_from_api);
//It's an array (assumption) of one or more objects
foreach (JsonElement element in foo.EnumerateArray())
{
//iterate through each object's properties
foreach (JsonProperty o in element.EnumerateObject())
{
//Assumption: value isn't a nested object/array, otherwise adjust...
Console.WriteLine($"{o.Name} = {o.Value}");
}
}
//Json.Net
//It's an array (assumption) of one or more objects
JArray deserialized = JsonConvert.DeserializeObject<JArray>(array_from_api);
foreach (JObject jo in deserialized)
{
//iterate through each JObject's properties
foreach (JProperty p in jo.Properties())
{
Console.WriteLine($"{p.Name} = {p.Value}");
}
}
Note: If the structure is more complex - more nesting, you'll have to do some checks and adjust accordingly (re: nested arrays, objects, etc)

Deserialize arbitrary JSON keys and raw values

Using JSON.Net I want to deserialize a JSON object that looks something like this:
"properties": {
"foo": 1,
"bar": "Fred",
"baz": [694481.61, 693638.0, 692624.65, 692354.54]
}
and end up with a dictionary<string,string> that looks like this:
{ "foo", "1" },
{ "bar", "\"Fred\"" },
{ "baz", "[694481.61, 693638.0, 692624.65, 692354.54]" }
The key points to note are:
The keys are not known in advance. (Hence the use of a dictionary rather than deserializing to a class with predefined properties foo, bar and baz.)
The value strings in the dictionary are the raw JSON text from the JSON stream. So strings include their encapsulating quotation marks, and arrays include their encapsulating square brackets.
What I have tried so far
Parse the values as JRaw objects. This would work if I knew the key names in advance: I could declare matching properties in my class as type JRaw - e.g. public JRaw foo { get; set; }. But the key names are not predefined so I can't.
Create a RawValueDictionaryConverter : JsonConverter custom converter that takes the stream and deserializes it directly to a dictionary<string,string>. I thought this would be straightforward, but the raw text of the original stream is not available inside ReadJson(); the stream has already been tokenized and ReadJson() only receives the interpreted token and token type.
Write my own tokenizer and forget about using Json.NET. It may come to this but I don't want to 'reinvent the wheel.'
You can deserialise to Dictionary<string, JRaw> which gets you half way there. For example:
//Example class
public class Root
{
public Dictionary<string, JRaw> properties { get; set; }
}
var json = "<from the question>";
var result = JsonConvert.DeserializeObject<Root>(json);
foreach (var property in result.Properties)
{
var value = property.Value.ToString();
}
If you still want the values as strings, you can convert like this:
var stringDictionary = result.Properties.ToDictionary(p => p.Key, p => p.ToString());

JSON.NET. Navigate to JArray object from its children

There's a JSON file like this:
{
"men": [
{
"name": "Jordan",
"phone": "333-333-33"
},
{
"name": "Timothey",
"phone": "444-444-44"
}
],
"women": [
{
"name": "Jordan",
"phone": "111-111-11"
},
{
"name": "Sasha",
"phone": "222-222-22"
}
]
}
I'd like to find all people whose name starts with J and find whether this person is a man or a woman.
var jsonProps = jsonDoc.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == prop);
var startsWithJ = jsonProps.Where(t => ((JProperty)t).Value.ToString().StartsWith("J"));
foreach (var person in startsWithJ)
{
Console.WriteLine(person.Value<string>());
Console.WriteLine(person.Parent.Parent.Value<string>());
}
The issue is that person.Parent.Parent is null and I expect it ot be JArray or at least JToken or at least some JToken so I can get its value.
Update:
I have like 100 types that use similar data structure. These collections (men, women) can be on any nesting level e.g. employees include men and women collection, or students include men and women collections. I can not create a strongly typed object for each of the types I have in my solution. Sometimes I need to remove objects from men/women collections and check whether the collection has any elements after this.
This isn't a direct answer to "how you query with LINQ to Json", but an approach i find easier using a strongly typed object.
By simply creating the following data structures (note the POCO's are decorated with JsonProperty which is a Json.NET attribute):
public class RootObject
{
public List<Person> Men { get; set; }
public List<Person> Women { get; set; }
}
public class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("phone")]
public string PhoneNumber { get; set; }
public Sex Sex { get; set }
}
public enum Sex
{
Man,
Women
}
Now, you can rather easily query what you need using LINQ to Objects:
var rootObject = JsonConvert.DeserializeObject<RootObject>(json);
foreach (var male in rootObject.Men)
{
male.Sex = Sex.Man;
}
foreach (var female in rootObject.Women)
{
female.Sex = Sex.Women;
}
var startsWithJ = rootObject.Men.Concat(rootObject.Women)
.Where(x => x.Name.StartsWith("J",
StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var personStartsWithJ in startsWithJ)
{
Console.WriteLine (personStartsWithJ.Name);
Console.WriteLine (personStartsWithJ.Sex);
}
Your code doesn't work because you are operating on all the name properties that have a value that starts with "J", not the person objects that contain name properties that have a value that starts with "J". So when you navigate upward, you aren't going far enough. The name property's parent is the person object. The person object's parent is the array, and the array's parent is the men (or women) JProperty. (Also, to find out whether the array's containing property's name is "men" or "women", you'll need to get its Name, not its Value.)
So, if you change your code to the following, you should get the result you expect:
foreach (JProperty nameProp in startsWithJ)
{
Console.WriteLine(nameProp.Value);
Console.WriteLine(((JProperty)nameProp.Parent.Parent.Parent).Name);
}
Fiddle: https://dotnetfiddle.net/J7WxiF
Personally, I find it a little easier to work at the JObject level rather than the JProperty level. Here is another way to get the same result:
var people = jsonDoc["men"]
.Children<JObject>()
.Union(jsonDoc["women"].Children<JObject>())
.Where(obj => obj["name"] != null && obj["name"].ToString().StartsWith("J"));
foreach (JObject person in people)
{
Console.WriteLine(person["name"]);
Console.WriteLine(((JProperty)person.Parent.Parent).Name);
}
Fiddle: https://dotnetfiddle.net/jzUOFT
If you are finding that you are always wanting to find the person's parent collection name in this way, you could go so far as to define an extension method:
public static class JHelper
{
public static bool IsMale(this JObject person)
{
return ((JProperty)person.Parent.Parent).Name == "men";
}
}
Then you can simply call IsMale() on the JObject. It makes the code a little more readable.
foreach (JObject person in people)
{
Console.WriteLine("Name: " + person["name"]);
Console.WriteLine("Gender: " + (person.IsMale() ? "male" : "female"));
}

How to parse JSON objects with numeric keys using JavaScriptSerializer

I have an object like below to be deserialized in C#. I am wondering how I can parse it. I tried following this example here, but am stumped on how I can get my class to recognize the key of each object (the 2 and the 3 below).
The JSON string below basically represents 2 transactions. I would like to convert each transaction representation into a Transaction object and put it into an array of Transaction object.
{
"2": {
"id": "2",
"user_id": "59",
"offer_id": "1234"
},
"3": {
"id": "3",
"user_id": "59",
"offer_id": "1234"
}
}
Here are my classes:
public class Transactions
{
// what goes here since the "key" field is always different?
}
public class Transaction
{
public int id { get; set; }
public int user_id { get; set; }
public int offer_id { get; set; }
}
It can be done with the JObject in JSON.Net library.
var transactions = JObject.Parse(json).PropertyValues()
.Select(o => o.ToObject<Transaction>());
This should do the trick.
Using the JavaScriptSerializer, you can handle this by deserializing into a Dictionary<string, Transaction>. From there you can convert it to an array pretty easily if you need to.
Example code:
string json = #"
{
""2"": {
""id"": ""2"",
""user_id"": ""59"",
""offer_id"": ""1234""
},
""3"": {
""id"": ""3"",
""user_id"": ""59"",
""offer_id"": ""1234""
}
}";
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, Transaction>>(json);
var transactions = dict.Select(kvp => kvp.Value).ToArray();
foreach (Transaction t in transactions)
{
Console.WriteLine(string.Format(
"id: {0}, user_id: {1}, offer_id: {2}", t.id, t.user_id, t.offer_id));
}
I ended up that serializing key-value pairs from Javascript to C# is impossible.
I've changed my code and and made a normal array of objects in JS and sent it to the server.
BUT it is possible to parse a key-value pairs form string variable to Dictionary in C# or to a list of KeyValuePair<x,x>
And it is also possible to build a key-value pair using C# and covert it to JSON or JS

Categories

Resources