How to get children data from json via linq - c#

I'm using json.net and i've a json data like that,
[
{
"ID":1098,
"Name":"JC",
"Issues":[
{
"PriorityLevel":"Low",
"State":"Open"
},
{
"PriorityLevel":"Low",
"State":"Open"
}
]
}
]
I just want to get childeren data from Issues via linq. I can reach parent but cannot children. If i reach children data directly i don't need to put more than one for loop.
Thank you.

You can just create a Json Object and extract the properties into an Anonymouse type that you can then query with Linq.
string response = #"[{
""ID"":1098,
""Name"":""JC"",
""Issues"":[
{
""PriorityLevel"":""Low"",
""State"":""Open""
},
{
""PriorityLevel"":""Low"",
""State"":""Open""
}
]}]";
var jsonObject = JObject.Parse(response);
var issues = jsonObject["Issues"].Select(x => new
{
PriorityLevel = (string)x.SelectToken("PriorityLevel"),
State = (string)x.SelectToken("State")
});
You use SelectToken to grab the children of Issues. Now you can query issues for whatever you want.
var lowPriorities = issues.Where(x => x.PriorityLevel == "Low");
Here is a direct link to the json.net page on "Deserializing Using LINQ Example".

Here you go
{
var json = #"[ {
""ID"":1098,
""Name"":""JC"",
""Issues"":[
{
""PriorityLevel"":""Low"",
""State"":""Open""
},
{
""PriorityLevel"":""Low"",
""State"":""Open""
}
]}]";
var a = JArray.Parse(json);
var issues = a.SelectMany (x => x["Issues"]);
var lowPriorities = issues.Where(x => ((string) x["PriorityLevel"]) == "Low");
}

Related

C# find JSON value based only on Key name through multiple levels of array

I have a variety of input JSON formatted data which all contain a particular key-name terminalSize. This is the only piece I know. The total number of JSON trees or the exact depth of terminalSize inside the JSON tree will forever be an unknown and subject to change.
I'm looking for a C# solution to loop through every child of the JSON string and find terminalSize then fetch the value.
I've tried this with success but it will only work if terminalSize is in the first level of the JSON:
var list = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(jsonString);
var dict = list.SelectMany(d => d).ToDictionary(p => p.Key, p => p.Value);
var terminal = dict["terminalSize"];
Example 1.
{
"status": "success",
"data": {
"terminalSize": 3766505.46,
"totalTerminalSize": 3766505.46
},
"message": null
}
Example 2.
{
"lastUpdated": 1588020678,
"terminalData": {
"terminalSize": "451679852",
"totalTerminalSize": "2100000000"
},
"terminalValueSeries": {
"8x7": 2.33,
"8x6": 3.73,
"8x5": 4.49,
"8x4": 3.68,
"8x3": 13998,
"8x2": 274936,
"8x1": 5.09
}
}
Example 3.
{
"terminalSize": "492612346.17",
"terminalStatus": "online"
}
You can parse your JSON to a JToken, then use SelectToken with the recursive descent JsonPath operator .. to get the terminalSize anywhere in the JSON:
var terminalSize = (double?) JToken.Parse(json).SelectToken("$..terminalSize");
Fiddle: https://dotnetfiddle.net/5ziYbP
If there might be multiple terminalSize keys in the JSON, for example if you had an array of terminals, you can use SelectTokens instead and put the terminal sizes into a Dictionary keyed by path:
var sizes = JToken.Parse(json4)
.SelectTokens("$..terminalSize")
.ToDictionary(t => t.Path, t => (double)t);
Fiddle: https://dotnetfiddle.net/ivSM88
You could also use linq and filter the JProperty collection based on JProperty.Name. For example
var result = JObject.Parse(jsonString)
.DescendantsAndSelf()
.OfType<JProperty>()
.Single(x=>x.Name.Equals("terminalSize"))
.Value;
You may parse your JSON into JObject, then recursively go through all properties and sub objects to find a terminalSize value. There is no need to deserialize the entire JSON into specific object
var json = JObject.Parse(jsonString);
var result = GetTerminalSize(json);
double GetTerminalSize(JObject input)
{
foreach (var property in input.Properties())
{
if (property.Name == "terminalSize")
return property.Value.Value<double>();
if (property.Value.Type == JTokenType.Object)
return GetTerminalSize((JObject) property.Value);
//not sure, if the is a need to handle an array
if (property.Value.Type == JTokenType.Array)
foreach (var item in (JArray) property.Value)
return GetTerminalSize((JObject) item);
}
return 0;
}
It returns a correct value for all 3 examples

Mongodb Bson type to Json

I am testing my asp.net core 2.2 web api with Postman. I write the JSON manually like this (httppatch):
{
"query": "{\"name\": \"foo\"}",
"update": [ "{\"$set\":{\"name\":\"foo2\"}}","{\"$set\":{\"path\": \"foo2 path\"}}" ]
}
Now I am thinking how can I build the patch body on the client side.
My question is how can I get the equivalent of this code in json to make it look like the one I write manually?
var query = Builders<T>.Filter.Eq(e => e.name, "foo");
var updates = Builders<T>.Update.Set(e => e.name, "foo2").Set(e => e.Path, "foo2 path");
I guess it's all about serialization, any idea how can I make it?
--Update--
I found this:
var serializerRegistry = BsonSerializer.SerializerRegistry;
var documentSerializer = serializerRegistry.GetSerializer<T>();
var upList = updates.Render(documentSerializer, serializerRegistry);
but it grabs only the last set it combines all sets in one (My bad, thanks to #Simon Mourier to pointing out my mistake !)
Here's the solution:
On the client side
// serializer
var serializerRegistry = BsonSerializer.SerializerRegistry;
var documentSerializer = serializerRegistry.GetSerializer<T>();
// filter and update
var filter = Builders<T>.Filter.Eq(e => e.Level, 2);
var updates = Builders<T>.Update
.Set(e => e.Name, "foo2")
.Set(e => e.Path, "foo2 path")
.Inc(e => e.Level, 1);
// get the string of the filter and the update
var filterString = filter.Render(documentSerializer, serializerRegistry);
var updateString = updates.Render(documentSerializer, serializerRegistry);
// instantiate patch object with properties to json
Patch patch = new Patch()
{
Query = filterString.ToJson(),
Update = updateString.ToJson()
};
// patch object to json
var patchJson = patch.ToJson();
On the server side
[HttpPatch]
public async Task<IActionResult> PatchOne([FromBody]Patch patch)
{
// don't need to ModelState.isValid, it's done on binding
try
{
var update = BsonDocument.Parse(patch.Update);
var filter = BsonDocument.Parse(patch.Query);
var result = await _serviceBase.UpdateOneAsync(filter, update);
...
}
catch (System.Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message.ToJson());
}
}
Global Modals (my solution structure)
public class Patch
{
[Required]
public string Query { get; set; }
[Required]
public string Update { get; set; }
}
Thanks for your help !!
I Find a way to see the query
you need make the query and save it in var
var query = Builders<T>.Filter.Eq(e => e.name, "foo");
var makeQuery = _collection.Find(query).ToString();
makeQuery have the value of Filter.Eq

Query Json Object dynamically

I am trying to implement a dynamic condition on a JSON Object.
After searching for a while, I am not able to find any resources which can meet my needs.
In the below fashion, I am getting the JsonObject dynamic string into jsonObject variable.
string inputFromAPI = client.GetStringAsync(URL).Result;
dynamic jsonObject = JValue.Parse(inputFromAPI);
I now have a condition which can change on need basis.For example I can have a condition which says
"inputFromAPI.DidWeCharge == true && inputFromAPI.DidWeHold == false"
The above line can change on the fly. Any inputs how to address this will be greatly appreciated.
I can't comment for clarification (don't have the rep yet), but if you need to be able to read the property name because it's changing you can do this. You would have to know the name though.
var JSONobj = JObject.Parse(json);
foreach (JToken child in JSONobj.Children())
{
var prop = child as JProperty;
var propertyName = prop.Name; // this will give you the name of the property
if (propertyName == "DidWeCharge")
{
var value = prop.Value; // Do something here with value?
}
if (propertyName == "DidWeHold")
{
var value = prop.Value; // Do something here with value?
}
var propertyType = prop.Value.Type; // this return the type as a JTokenType enum.
}
I don't know how nested your JSON is, so you may have to traverse further down with another foreach on the child by doing child.Children().
You can use ExpandoObject:
var expandoObj = JsonConvert.DeserializeObject<ExpandoObject>(jsonObject);
expandoObj.yourProperty
JsonConvert is from Newtonsoft.Json package.
You may be able to use Jpath:
using Newtonsoft.Json -
....
var json = #"
{
stuff : [
{
value : 1
},
{
value : 2
}
]
}";
var token = JToken.Parse(json);
var something = token.SelectTokens("$.stuff[?(#.value == 1)]").ToList();

Update a nested Array in Mongo with c#

I have a document like this
{
"_id": "63dafa72f21d48312d8ca405",
"tasks": [{
"_ref": "63d8d8d01beb0b606314e322",
"data": {
"values": [{
"key": "Deadline",
"value": "2014-10-13"
}]
}
}, {
"_ref": "84dd046c6695e32322d842f5",
"data": {
"values": []
}
}]
}
Now I want to update the value inside values which is inside data if the _ref field do match my input.
My code so far:
public bool updateProject(Project dbPro, Project pro)
{
var collection = db.GetCollection<BsonDocument>("projects");
var filter = Builders<BsonDocument>.Filter.Eq("_id", ObjectId.Parse( dbPro.Id));
var update = Builders<BsonDocument>.Update.AddToSetEach("tasks", pro.Tasks);
var result = collection.UpdateOne(filter, update);
if (result.IsModifiedCountAvailable)
{
if (result.ModifiedCount == 1)
{
return true;
}
}
return false;
}
At the moment this code does only append the documents as new tasks instead to append the values to the matching tasks. Maybe someone has an idea how to achieve this behavior?
UPDATE
I tried it like #Shane Oborn said. But its still not working for me.
var collection = db.GetCollection<BsonDocument>("projects");
var filter = Builders<BsonDocument>.Filter.Eq("_id", ObjectId.Parse( dbPro.Id));
var update = Builders<BsonDocument>.Update.Push("tags", buildBsonArrayFromTags(pro.Tags));
var result = collection.UpdateOne(filter, update);
if (result.IsModifiedCountAvailable)
{
if (result.ModifiedCount == 1)
{
return true;
}
}
return false;
}
Instead to override the data it appends an array to my array.
UPDATE
OK instead of push i did need set. And it worked then.
I don't have the exact code accessible, but close. I have a method that performs "upserts" (which "adds" if new, or "updates" if existing). This should get you close:
// The variable "doc" below is a BsonDocument
var updateRequests = new List<WriteModel<BsonDocument>>();
updateRequests.Add(new ReplaceOneModel<BsonDocument>(
CreateBsonDocumentFilterDefinition(filterKeyName, filterKeyValue), doc)
{
IsUpsert = true
});
var writeResult = await collection.BulkWriteAsync(updateRequests);
The key objects here for you are "ReplaceOneModel" and the "IsUpsert" property for the filter definition.
Good luck!
UPDATE:
Another method I have that does updates in subdocuments looks like this:
// Below, "subDocument" is a BsonDocument, and "subDocArrayName" is a string
// that should match the name of the array that contains your sub-document
// that will be updated.
var collection = _database.GetCollection<BsonDocument>(collectionName);
var builder = Builders<BsonDocument>.Update;
var update = builder.Push(subDocArrayName, subDocument);
await collection.UpdateOneAsync(CreateBsonDocumentFilterDefinition(filterKeyName, filterKeyValue), update);

Deserializing JSON response without creating a class

From the result of an API call I have a large amount of JSON to process.
I currently have this
Object convertObj = JsonConvert.DeserializeObject(responseFromServer);
I am aware that I could do something like
Movie m = JsonConvert.DeserializeObject<Movie>(responseFromServer);
And then use it like
m.FieldName
m.AnotherField
//etc
Ideally I would like to do something like
var itemName = convertObj["Name"];
to get the first Name value for the first item in the list.
Is this possible, or do I have to create a class to deserialize to?
The reason I do not want to create the class is I am not the owner of the API and the field structure may change.
Edit.
Okay so I created the class as it seems the best approach, but is there a way to deserialize the JSON into a list?
var sessionScans = new List<SessionScan>();
sessionScans = JsonConvert.DeserializeObject<SessionScan>(responseFromServer);
Complains that it cannot convert SessionScan to generic list.
No need to use dynamic, you can simply use JToken which is already does what you expect:
var json = #"
{
""someObj"": 5
}
";
var result = JsonConvert.DeserializeObject<JToken>(json);
var t = result["someObj"]; //contains 5
With .NET 6, this can be done as below,
using System.Text.Json;
using System.Text.Json.Nodes;
string jsonString = #"some json string here";
JsonNode forecastNode = JsonNode.Parse(jsonString)!;
int temperatureInt = (int)forecastNode!["Temperature"]!;
Console.WriteLine($"Value={temperatureInt}");
//for nested elements, you can access as below
int someVal = someNode!["someParent"]["childId"]!.ToString();
Refer this MS docs page for more samples - create object using initializers, make changes to DOM, deserialize subsection of a JSON payload.
You can try with JObject.Parse :
dynamic convertObj = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = convertObj.Name;
string address = convertObj.Address.City;
The below example can deserialize JSON to a list of anonymous objects using NewtonSoft.Json's DeserializeAnonymousType method.
var json = System.IO.File.ReadAllText(#"C:\TestJSONFiles\yourJSONFile.json");
var fooDefinition = new { FieldName = "", AnotherField = 0 }; // type with fields of string, int
var fooListDefinition = new []{ fooDefinition }.ToList();
var foos = JsonConvert.DeserializeAnonymousType(json, fooListDefinition);
You can use Json.NET's LINQ to JSON API
JObject o = JObject.Parse(jsonString);
string prop = (string)o["prop"];
Use Newtonsoft.Json
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
var json = "[{'a':'aaa','b':'bbb','c':'ccc'},{'a':'aa','b':'bb','c':'cc'}]";
var ja = (JArray)JsonConvert.DeserializeObject(json);
var jo = (JObject) ja[0];
Console.WriteLine(jo["a"]);
I had this problem working with unknown APIs then I decide to come over this problem using this approach, I'm writing down here my test case:
[TestMethod]
public void JsonDocumentDeserialize()
{
string jsonResult = #"{
""status"": ""INTERNAL_SERVER_ERROR"",
""timestamp"": ""09-09-2019 11:00:24"",
""message"": ""documentUri is required.""
}";
var jDoc = JsonDocument.Parse(jsonResult);
if (jDoc.RootElement.TryGetProperty("message", out JsonElement message))
{
Assert.IsTrue(message.GetString() == "documentUri is required.");
}
}
it worked for me because first I was looking to find a way to use dynamic type as it's mentioned in Azure Function HTTPTrigger. But I found this approach most useful and robust.
Microsoft Reference

Categories