Json children properties to dictionary - c#

I'm trying to extract a list of dates for a property of all children nodes on a JToken but cannot get the syntax correct.
I want to get a list of dates in property "timeStamp": "2013-09-11T00:30:00Z" so I can determine the min/max dates for all child nodes.
I've tried the following which returns an anonymous type and makes it difficult to use the returned object.
var timeStamps = Jarr.Select(x => new
{
timeStamp = (DateTime)x.SelectToken("timeStamp")
});
How can I get say a List<string> or List<DateTime> only of all child timestamps?
Is it possible to get a Dictionary<string, DateTime> of id, timestamp?
The Json looks like this, so essentially from LEVEL1 I want to check all children, children of children for the same property.
{
"children": [
{
"type": "LEVEL2",
"name": "Item1",
"id": "1.7193",
"timeStamp": "2013-09-11T00:30:00Z",
},
{
"type": "LEVEL2",
"name": "Item2",
"id": "1.7194",
"timeStamp": "2013-09-11T00:30:00Z",
},
{
"type": "LEVEL2",
"name": "Item3",
"id": "1.7191",
"timeStamp": "2013-09-11T00:30:00Z",
}
],
"type": "LEVEL1",
"name": "Stock-FRT54443",
"id": "1000145",
"countryCode": "en"
}
and method
void AddNodes(TreeView treeView, JObject jObj, TreeNodeCollection parent)
{
JToken Jarr = null;
Dictionary<string, string> marketProperties = new Dictionary<string, string>();
foreach (var property in jObj.Properties())
{
if (property.Name == "children")
{
Jarr = property.Value;
}
else
{
string key = property.Name;
string prop = property.Value.ToString();
marketProperties.Add(key, prop);
}
}
if (marketProperties["type"] == "LEVEL1")
{
//Not working!
var timeStamps = Jarr["timeStamp"].Values<string>();
}
}

When you use the 'new' keyword it's going to create an anonymous type. You're creating a list of objects with a timestamp property rather than a list of DateTimes. All you need to do to get your date list is to change it to:
DateTime timeStamps = Jarr.Select(x => (DateTime)x.SelectToken("timeStamp")).ToList();
It's also possible to get a dictionary:
Dict<string,DateTime> dictionary = Jarr["children"].ToDictionary(x=>x["Id"].ToString(),x=>(DateTime)(x["timeStamp"]));
The second is untested but should give you the general idea.

Related

Add a list in an existing JSON value

So I have below call to a method where my argument is a json string of this type
var jsonWithSearchData = await querySearchData(jsonOut);
jsonOut ->
[
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": "",
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
]
The querySearchData() returns me two list something like this :["P123","P124","P987"] and ["Ba123","KO817","Daaa112"]
I want to add this list in my r:searchData key above. The key inside my searchData i.e. r:Porelation and ClassA and ClassB remains static. So I would like my searchData in my input Json to finally become something like this.
"r:searchData": {
"r:Porelation":{
"ClassA": ["P123","P124","P987"],
"ClassB": ["Ba123","KO817","Daaa112"]
}
},
How can I do this? What I tried:
JArray jfinObject = JArray.Parse(jobjects);
jfinObject["r:searchData"]["r:relations"]["ClassA"] = JArray.Parse(ListofCode.ToString());
And I get below error:
System.Private.CoreLib: Exception while executing function: Function1.
Newtonsoft.Json: Accessed JArray values with invalid key value:
"r:searchData". Int32 array index expected.
There are a few ways you can add a node/object/array to existing json.
One option is to use Linq-to-Json to build up the correct model.
Assuming you have the json string described in your question, the below code will add your desired json to the r:searchData node:
var arr = JArray.Parse(json); // the json string
var payloadNode = arr[0]["data"]["payload"];
// use linq-to-json to create the correct object
var objectToAdd = new JObject(
new JProperty("r:Porelation",
new JObject(
new JProperty("r:ClassA", array1),
new JProperty("r:ClassB", array2))));
payloadNode["r:searchData"] = objectToAdd;
where array1 and array2 above could come from a linq query (or just standard arrays).
// Output:
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": {
"r:Porelation": {
"r:ClassA": [
"P123",
"P456"
],
"r:ClassB": [
"Ba123",
"Ba456"
]
}
},
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
Online demo
Another option is to create the json from an object, which could be achieved using JToken.FromObject(). However, this will only work if you have property names which are also valid for C# properties. So, this won't work for your desired property names as they contain invalid characters for C# properties, but it might help someone else:
// create JToken with required data using anonymous type
var porelation = JToken.FromObject(new
{
ClassA = new[] { "P123", "P456" }, // replace with your arrays here
ClassB = new[] { "Ba123", "Ba456" } // and here
});
// create JObject and add to original array
var newObjectToAdd = new JObject(new JProperty("r:Porelation", porelation));
payloadNode["r:searchData"] = newObjectToAdd;

How to remove parent element from json in c#

How to covert the below json
{"data":{"id":12,"name":"jeremy","email":"jeremy#test.com"}}
to
{"id":12,"name":"jeremy","email":"jeremy#test.com"}
I want to remove the "data" element from json.
With json.net it's fairly straightforward
var input = "{\"data\":{\"id\":12,\"name\":\"jeremy\",\"email\":\"jeremy#test.com\"}}";
var result = JObject.Parse(input)["data"].ToString(Formatting.None);
Console.WriteLine(result);
Note : Formatting.None is only to preserve the formatting you had in your original example
Or Text.Json
var result = JsonDocument.Parse(input).RootElement.GetProperty("data").ToString();
Output
{"id":12,"name":"jeremy","email":"jeremy#test.com"}
Additional Resources
JObject.Parse Method (String)
Load a JObject from a string that contains JSON.
JObject.Item Property (String)
Gets or sets the JToken with the specified property name.
JToken.ToString Method (Formatting,JsonConverter[])
Returns the JSON for this token using the given formatting and
converters.
Formatting Enumeration
None 0 No special formatting is applied.
Text.Json
JsonDocument.Parse Method
Provides a mechanism for examining the structural content of a JSON
value without automatically instantiating data values.
JsonDocument.RootElement Property
Gets the root element of this JSON document
JsonElement.GetProperty Method
Gets a JsonElement representing the value of a required property
identified by propertyName.
I have a follow up question on a scenario where I don't want to remove the root element.
{
"keepMe": {
"removeMe": [
{
"id": "1",
"name": "Foo",
"email": "Foo#email.com"
},
{
"id": "2",
"name": "Bar",
"email": "Bar#email.com"
}
]
}
But I wanted it to look like
{
"keepMe": {
{
"id": "1",
"name": "Foo",
"email": "Foo#email.com"
},
{
"id": "2",
"name": "Bar",
"email": "Bar#email.com"
}
}
Using below would not work. Is there another way to do this?
var result = JObject.Parse(input)["keepMe"]["removeMe"].ToString(Formatting.None);
//{
// "id": "1",
// "name": "Foo",
// "email": "Foo#email.com"
//},
//{
// "id": "2",
// "name": "Bar",
// "email": "Bar#email.com"
//}
var result = JObject.Parse(input)["removeMe"].ToString(Formatting.None); //Null Reference
Found the answer using the SelectToken and Replace keyword
var jObj = JObject.Parse(input);
jObj.SelectToken("keepMe").Replace(jObj.SelectToken("keepMe.removeMe"));
string result = jObj.ToString();

Given a JSON value, how do I get the parent's sibling value?

I have a .NET Core 3.1 C# application reading the following JSON doc:
{
"info": {
"_postman_id": "b"
},
"item": [
{
"name": "GetEntityById via APIM",
"item": [
{
"name": "Call 1",
"url": {
"raw": "urlforcall1"
}
},
{
"name": "Call 2",
"url": {
"raw": "urlforcall2"
}
}
]
}
]
}
I want to select the value for each item\item\name and each item\item\url\raw.
So, I'd like to end up with "Call 1":"urlforcall1" and "Call 2":"urlforcall2".
I've been playing around and can grab the value from the raw token with the following:
var jObject = JObject.Parse(jsonString);
var urls = jObject.SelectTokens("..raw");
How can I grab the value from its parent's sibling, name?
I hope this code will help you
using Newtonsoft.Json.Linq;
using System;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string json = #"
{
'info': {
'_postman_id': 'b'
},
'item': [
{
'name': 'GetEntityById via APIM',
'item': [
{
'name': 'Call 1',
'url': {
'raw': 'urlforcall1',
}
},
{
'name': 'Call 2',
'url': {
'raw': 'urlforcall2',
}
}
]
}
]
}";
dynamic d = JObject.Parse(json);
foreach(var item in d.item)
{
foreach(var innerItem in item.item)
{
Console.WriteLine($"'{innerItem.name}' : '{innerItem.url.raw}'");
}
}
}
}
}
Can be tested here https://dotnetfiddle.net/xDr90O
To answer your question directly, if you have a JToken you can navigate upward from there using the Parent property. In your case you would need to use it four times to get to the level you want:
The parent of the JValue representing the call URL string is a JProperty with the name raw
The parent of that JProperty is a JObject
The parent of that JObject is a JProperty with the name url
The parent of that JProperty is a JObject, which also contains the name property
From there you can navigate back down using indexer syntax to get the value of name.
So, you would end up with this:
var jObject = JObject.Parse(jsonString);
foreach (JToken raw in jObject.SelectTokens("..raw"))
{
string callName = (string)raw.Parent.Parent.Parent.Parent["name"];
string urlForCall = (string)raw;
}
You may flatten inner item array using SelectMany method into one sequence (since outer item is also an array), then get name and raw values directly by key
var jObject = JObject.Parse(jsonString);
var innerItems = jObject["item"]?.SelectMany(t => t["item"]);
foreach (var item in innerItems)
{
var name = item["name"];
var raw = item["url"]?["raw"];
}

Last entry in dictionary overwrites first written entry when adding dictionary to json file [duplicate]

This question already has an answer here:
Adding object to JArray overwriting first element
(1 answer)
Closed 4 years ago.
I have an existing json file which looks like this:
{
"players": [],
"games": []
}
I want to add objects to the players array so it looks like this:
{
"players": [
{
"name": "Peter",
"checksum": "6fa95b1427af77b3d769ae9cb853382f"
},
{
"name": "John",
"checksum": "61409aa1fd47d4a5332de23cbf59a36f"
},
{
"name": "Bob",
"checksum": "2fc1c0beb992cd7096975cfebf9d5c3b"
}
],
"games": []
}
Players are stored in a global Dictionary<string, string>. But in my implementation, the next element in the dictionary overwrites the first written element so when the loop is at John the next element would be Bob and then Peter gets replaced by Bob. The result of this looks like this:
{
"players": [
{
"name": "Bob",
"checksum": "2fc1c0beb992cd7096975cfebf9d5c3b"
},
{
"name": "John",
"checksum": "61409aa1fd47d4a5332de23cbf59a36f"
},
{
"name": "Bob",
"checksum": "2fc1c0beb992cd7096975cfebf9d5c3b"
}
],
"games": []
}
This is my code:
string json = File.ReadAllText("file.json");
JObject jsonObject = JObject.Parse(json);
JArray jsonPlayerArray = (JArray) jsonObject["players"];
JObject newPlayerEntry = new JObject();
var sortedDict = PlayerChecksumDict.OrderBy(x => x.Key);
foreach (var item in sortedDict)
{
newPlayerEntry["name"] = item.Key;
newPlayerEntry["checksum"] = item.Value;
jsonPlayerArray.Add(newPlayerEntry);
}
string modifiedJson = jsonObject.ToString(Formatting.Indented);
File.WriteAllText("file-modified.json", modifiedJson);
(Similar questions to this have been asked several times, but I haven't been able to find a duplicate, so I figure it's worth answering instead.)
You're creating a single JObject instance, then modifying it multiple times within your loop. Each iteration will overwrite the data set in the previous iteration. Your JArray ends up with lots of references to the same JObject.
Instead, you need to create a new JObject for each entry in your dictionary. To do this, you just need to move the declaration of newPlayerEntry into the loop:
var sortedDict = PlayerChecksumDict.OrderBy(x => x.Key);
foreach (var item in sortedDict)
{
JObject newPlayerEntry = new JObject();
newPlayerEntry["name"] = item.Key;
newPlayerEntry["checksum"] = item.Value;
jsonPlayerArray.Add(newPlayerEntry);
}
Now each element in the JArray will be a reference to a different JObject.

Populate KendoUi Treeview with RavenDB documents

I am using MVC4 and C#.
I have a KendoUI Treeview and I'd like to populate it with data from RavenDB.
In the demo they use this:
public JsonResult Employees(int? id)
{
var dataContext = new NorthwindDataContext();
var employees = from e in dataContext.Employees
where (id.HasValue ? e.ReportsTo == id : e.ReportsTo == null)
select new {
id = e.EmployeeID,
Name = e.FirstName + " " + e.LastName,
hasChildren = e.Employees.Any()
};
return Json(employees, JsonRequestBehavior.AllowGet);
}
Notice the argument "id" - it's an int. In RavenDb document ID's are strings eg. myDco/231...
Imagine this is a standard document:
{
"CreationDate": "12/12/2012 8:07:59 PM",
"Name": "jkljklk",
"Type": "kljkljkljkljkl",
"Description": "ljkljklj",
"CreatedByUser": "ljklklkljklj",
"Deleted": false,
"Status": "NEW",
"Parent": "product/546"
}
How would I populate the treeview?
There should not be any problem to use string instead of that int. You just need to fetch the needed records depending on that string parameter passed to the Action method.
I figured this out.
I changed my RavenDB Document to include a list of all its children:
{
"CreationDate": "12/12/2012 9:33:34 PM",
"Name": "hohoho",
"Type": "hohohoh",
"Description": "hohohoh",
"CreatedByUser": "hohohoh",
"Deleted": false,
"Status": "NEW",
"Parent": "ohohohoh",
"Children": [
"item/1",
"item/2",
"item/3"
]
}
I then returned a list of my items to the View via the controller and then iterated through them, appending all the children to their correct parent nodes:
#(Html.Kendo().TreeView().Name("TreeView").DragAndDrop(true)
.Items(treeview =>
{
foreach (var myItem in Model)
{
var myItemName = myItem.Name;
var children = myItem.Children;
treeview.Add().Text(myItemName ).Expanded(false).Items(branch =>
{
if (children != null)
{
foreach (var child in children)
{
branch.Add().Text(child);
}
}
});
}
}
))
Anyway, thanks for the responses :)
Hope this helps someone else one day.

Categories