JSON.NET. Navigate to JArray object from its children - c#

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"));
}

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)

C# Iterate over a JSON object

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();
}
}

Push an item to a deeply nested array in MongoDb

Is it possible (preferably using the C# Builders) to add a new item to a deeply nested array I.e. an array within an array within an array.
My data model looks something like :
public class Company
{
public string Id { get; set; }
public string Name { get; set; }
public IEnumerable<Department> Departments { get; set; }
}
public class Department
{
public string Id { get; set; }
public string Name { get; set; }
public IEnumerable<Managers> Managers { get; set; }
}
public class Manager
{
public string Id { get; set; }
public string Name { get; set; }
public IEnumerable<Employee> Employees { get; set; }
}
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
}
Which translates to:
{
"Id": 12345,
"Name": "Company Ltd",
"Departments": [
{
"Id": 1,
"Name": "Development",
"Managers" : [
{
"Id" : 5555,
"Name" : "The Boss",
"Employees": [
{
"Id" : 123,
"Name" : "Developer 1"
},
{
"Id" : 124,
"Name" : "Developer 2"
}
]
}
]
}
]
}
If I wanted to add another employee under a specific manager how would I go about doing that?
In order to push to a nested array, you must make use of the positional operator $ in order to specify a matching outer array element to apply the operation to. For example:
db.collection.update(
{"my_array._id": myTargetId},
{$push: {"my_array.$.my_inner_array": myArrayElem}}
);
This breaks down, however, for traversing nested arrays--that is, you can only use the positional operator on the single array, not any nested ones. This is a well-defined problem as noted in the MongoDB documentation.
If you absolutely need to perform these kinds of nested array operations, then you have a couple of options available to you:
The first, and preferred, is to update your document structure and avoid nesting arrays more than one level deep. This will avoid the issue altogether, but will require any existing data to be migrated to the new structure and additional efforts to be made to structure the data in the way you need on the fly on retrieval. Separate client and server representations of your data will end up being required.
The second is to perform a series of less-reliable steps:
1. Retrieve the original document.
2. Locate the indexes for each array where your target element is located manually.
3. Attempt an update on the specific index chain and attempt to match that index chain as well.
4. Check the result of the update attempt--if it fails, then it's possible that the document was changed while the indexes were being calculated.
For example, if you wanted to update manager with ID 5555 to have the additional employee, you'd perform the following query after retrieving the indexes:
// Index chain found to be Departments.0 and Managers.0
db.collection.update(
{
"Id": 12345,
"Departments.0.Managers.0.Id": 5555 // Specify index chain 0,0 and ensure that our target still has Id 5555.
},
{ $push: {
"Departments.0.Managers.0.Employees": myNewEmployee // Push to index chain 0,0
}}
);
Use positional operator for each of the arrays except the one you want to push to.
Use array filters in update options to specify the department and manager ids. The letter used in the array filters should match the letter used as the positional operator in the update definition. So "d.Id" -> "Departments.$[d]"
If you want to match on more than one property you can use a dictionary in the array filter.
private IMongoCollection<Company> _collection;
public async Task AddEmployee()
{
var filter = Builders<Company>.Filter.Where(d => d.Id == "companyId");
var update = Builders<Company>.Update
.Push("Departments.$[d].Managers.$[m].Employees", new Employee { Id = "employeeId", Name = "employeeName" });
var updateOptions = new UpdateOptions
{
ArrayFilters = new List<ArrayFilterDefinition>
{
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("d.Id", "departmentId")),
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("m.Id", "managerId")),
}
};
await _collection.UpdateOneAsync(filter, update, updateOptions);
}
The problem here is that you need to use strings in the update definition and filters but not sure how to manage it without strings.
To remove an employee from the array is similar but you will have to specify an extra filter for the employee that you want to remove.
public async Task FireEmployee()
{
var filter = Builders<Company>.Filter.Where(d => d.Id == "companyId");
var employeeFilter = Builders<Employee>.Filter.Where(e => e.Id == "employeeId");
var update = Builders<Company>.Update
.PullFilter("Departments.$[d].Managers.$[m].Employees", employeeFilter);
var updateOptions = new UpdateOptions
{
ArrayFilters = new List<ArrayFilterDefinition>
{
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("d.Id", "departmentId")),
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("m.Id", "managerId")),
}
};
await _collection.UpdateOneAsync(filter, update, updateOptions);
}

What is the correct way to parse this JSON object in C#?

This is the JSON object I am receiving from a GET request:
{
"data": {
"valve_maker": [],
"water_volume": [
"15L",
"20L",
"..."
],
"cylinder_manufacturer": [
"Tianhai"
],
"qc_stamp": [
"TS"
],
"reference_standard": [
"GB 5099"
],
"production_licence": [
"TS2210752-2016"
],
"valve_production_licence": [
"TSF210030"
],
"rate_of_residual_deformation": {
"1": "<3%",
"2": "<10%"
},
"material_number": {
"1": "30CrMo",
"2": "34CrMo4",
"3": "..."
},
"heat_treatment": {
"1": "...",
"2": "..."
},
"drawing_number": {
"1": "...",
"2": "..."
},
"cylinder_thickness": []
}
right now, I am able to parse JSON objects with a simpler structure like :
{
"data": [
{
"gas_id": "ID of the gas",
"gas_name": "Gas name"
}
]
by using something like this:
private void jsonparsegas(string res)
{
JObject par = JObject.Parse(res);
foreach (JToken data in par["data"].Children())
{
string id = data["gas_id"].ToString();
string name = data["gas_name"].ToString();
if (this.cmbCylType.Items.Contains(name) == false)
{
this.cmbCylType.Items.Add(name);
}
}
}
When I try to apply the same thing to the more complicated JSON object, I get an error:
private void jsonparsecoc(string res)
{
//JObject par = JObject.Parse(res);
var jObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(res);
foreach (var child in jObj["data"].Children())
{
string vMaker = child["valve_maker"].ToString(); //error thrown here right away
string wVolume = child["water_volume"].ToString();
string cMan = child["cylinder_manufacturer"].ToString();
string QC = child["qc_stamp"].ToString();
string rStandard = child["reference_standard"].ToString();
string pLicence = child["production_licence"].ToString();
string VPL = child["valve_production_licence"].ToString();
string rrd = child["rate_of_residual_deformation"].ToString();
string mNum = child["material_number"].ToString();
string hTreatment = child["heat_treatment"].ToString();
string dNum = child["drawing_number"].ToString();
string cThick = child["cylinder_thickness"].ToString();
}
Cannot access child value on Newtonsoft.Json.Linq.JProperty
I have tried a few different things I found on StackOverflow, but I don't really understand how Deserializing of the objects works. The simpler parsing works just fine, and allows me to add all "gas_name"s that I receive from my GET request to a combobox. The format the first "valve_maker" child of "data" seems to have the same structure as "gas_id" or "gas_name" in the more similar JSON object, but this is where I receive the error right away. If I had to guess at a cause for the error, I'd say it has something to do with the difference between using
"valve_maker": []
and using
"gas_id": "ID of the gas"
in the objects. also I notice "data" is followed by [] brackets in the simpler one, and {} in the more complicated one.
If anyone could link to some good reading material, or offer a good explanation of a solution/what's going on, I'd really appreciate it.
This part is the key to the problem you're having:
in the objects. also I notice "data" is followed by [] brackets in the simpler one, and {} in the more complicated one.
In JSON,
[] brackets enclose an array
{} brackets enclose an object
In both code examples, you are digging into the object by looping through the results using par["data"].Children(). To be consistent with the JSON model, JSON.NET defines different behavior for resolving children of objects and arrays. The children of an object are its properties and the children of an array are its items.
In your code, the input to jsonparsegas is an array of simple objects with 2 properties where the input to jsonparsecoc is a single complex objects with many properties.
In jsonparsegas, the Children() call gives you an array of all the simple gas objects. You loop through these objects and extract the values of "gas_id" and "gas_name" for each object. In your example data, there only happens to be one gas object so your code only executes once.
In jsonparsecoc, the Children() call actually gives you property values for the properties of the complex object, since the result is an object and not an array. So, when you loop through this result you can't access things like "valve_maker", because they are defined on the complex object and you have already stepped into the value for valve_maker then this executes.
The solution is simple. Don't loop through the properties in jsonparsecoc. Instead of foreach(var child in jObj["data"].Children()) you need something like var child = jObj["data"];. This will give you the reference to the object that actually contains each of the properties you are trying to access.
#smartcaveman did a good job of explaining what is going wrong with your code. However, you might find your data a little easier to process if you defined strongly-typed classes for it like this:
class RootObject
{
public Data Data { get; set; }
}
class Data
{
[JsonProperty("valve_maker")]
public List<string> ValveMaker { get; set; }
[JsonProperty("water_volume")]
public List<string> WaterVolume { get; set; }
[JsonProperty("cylinder_manufacturer")]
public List<string> CylinderManufacturer { get; set; }
[JsonProperty("qc_stamp")]
public List<string> QCStamp { get; set; }
[JsonProperty("reference_standard")]
public List<string> ReferenceStandard { get; set; }
[JsonProperty("production_licence")]
public List<string> ProductionLicense { get; set; }
[JsonProperty("valve_production_licence")]
public List<string> ValveProductionLicense { get; set; }
[JsonProperty("rate_of_residual_deformation")]
public Dictionary<string, string> RateOfResidualDeformation { get; set; }
[JsonProperty("material_number")]
public Dictionary<string, string> MaterialNumber { get; set; }
[JsonProperty("heat_treatment")]
public Dictionary<string, string> HeatTreatment { get; set; }
[JsonProperty("drawing_number")]
public Dictionary<string, string> DrawingNumber { get; set; }
[JsonProperty("cylinder_thickness")]
public List<string> CylinderThickness { get; set; }
}
You can deserialize the JSON to your classes like this:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
Demo here: https://dotnetfiddle.net/p0D7ze
public class Test{
public Data data{get;set;}
}
public class Data{
public List<string> Value_maker{get;set;}
public List<String> Water_Volume{get;set;}
public List<String> cylinder_manufacturer{get;set;}
}
Create Class structure Like that and after that use to deserialize
Jsonconvert.DeserializeObject(JsonString)
it will convert json into proper object, keep in mind structure should be proper and property name should be same as your json property
Hope It will Help you

Categories

Resources