Add a JToken to a specific JsonPath in a JObject - c#

I have a JObject that looks like this..
{
"address": [
{
"addressLine1": "123",
"addressLine2": "124"
},
{
"addressLine1": "123",
"addressLine2": "144"
}
]
}
for JsonPath address[2], I want to add the following JObject to the address array.
{
"addressLine1": "123",
"addressLine2": "144"
}
I want to do something like json.TryAdd(jsonPath, value);
If there was an object at index 2 i would have easily done
var token = json.SelectToken(jsonPath);
if (token != null && token .Type != JTokenType.Null)
{
token .Replace(value);
}
but since that index does not exist I'll get null as the value of token

As we mentioned on comment section, you can not add/replace item to index if index does not exist. In that case you need to add item as a new member of JArray instead, or you can use Insert to add item at the specified index :
JObject value = new JObject();
value.Add("addressLine1", "123");
value.Add("addressLine2", "144");
JObject o = JObject.Parse(json);
int index = 2;
JToken token = o.SelectToken("address[" + index + "]");
if (token != null && token.Type != JTokenType.Null)
{
token.Replace(value);
}
else //If index does not exist than add to Array
{
JArray jsonArray = (JArray)o["address"];
jsonArray.Add(value);
//jsonArray.Insert(index, value); Or you can use Insert
}

Related

Pick only one record from a JSON array in C#

I'm trying to extract a record from a JSON array but it doesn't seem to work.
Here my code :
DataTable table = ConvertJsonToDatatable(responseBody);
System.Windows.Forms.MessageBox.Show(table.Columns[1].ToString(), "transformation");
The MessageBox isn't showing. I have checked responseBody and the variable isn't empty at all.
Here the structure of this variable (and the JSON array rear)
{
"data":
[
[
1651217520000,
1.0562,
1.0562,
1.056,
1.0561,
0,
0
],
[
1651217580000,
1.0561,
1.0563,
1.0561,
1.0561,
0,
0
]
],
"events": null
}
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
var jsonArray = new JArray();
foreach (JObject row in linqArray.Children<JObject>())
{
var createRow = new JObject();
foreach (JProperty column in row.Properties())
{
// Only include JValue types
if (column.Value is JValue)
{
createRow.Add(column.Name, column.Value);
}
}
jsonArray.Add(createRow);
}
return JsonConvert.DeserializeObject<DataTable>(jsonArray.ToString());
}
Does anyone have an idea of how to extract/pick one value from this array (which is a string in my code) ?
Have a nice week end everyone and thanks in advance
you have to fix table creating code
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
//or maybe this would be enough
var linqArray = JObject.Parse(jsonString)["data"];
var jsonArray = new JArray();
foreach (var row in (JArray)linqArray)
{
var createdRow = new JObject();
var i = 0;
foreach (var item in row)
{
i++;
createdRow.Add("col" + i.ToString(), (string)item);
}
jsonArray.Add(createdRow);
}
return jsonArray.ToObject<DataTable>();
}
how to use
DataTable table = ConvertJsonToDatatable(responseBody);
string val = table.Rows[0].Field<string>("col2");
System.Windows.Forms.MessageBox.Show(val, "transformation");
i probably would be marked negative, but i try to explain how it looks like inside. i made example to show tow to get back list of arrays it might visualize for you.
void Main()
{
string json = "{\"data\":[[1651217520000,1.0562,1.0562,1.056,1.0561,0,0],[1651217580000,1.0561,1.0563,1.0561,1.0561,0,0]],\"events\":null}";
var obj = JObject.Parse(json);
foreach (JToken token in obj.FindTokens("data"))
{
foreach (JArray row in JArray.Parse(token.ToString()))
{
row.Dump();
row[0].Dump("first element");
}
}
}
public static class JsonExtensions
{
public static List<JToken> FindTokens(this JToken containerToken, string name)
{
List<JToken> matches = new List<JToken>();
FindTokens(containerToken, name, matches);
return matches;
}
private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Name == name)
{
matches.Add(child.Value);
}
FindTokens(child.Value, name, matches);
}
}
else if (containerToken.Type == JTokenType.Array)
{
foreach (JToken child in containerToken.Children())
{
FindTokens(child, name, matches);
}
}
}
}
result would be an array of jarray
so you can build you DataTable rows
.Dump() is a left over from linqpad. Good tool. Same as console.write

iterate through JSON?

Can I Iterate (recursive) through this JSON?
My Problem are not the JObject rather the JArray.
This is a multi-nested JSON that I would like to go through and from which I would like to read the key and value separately.
{
"DataValid": 1,
"Identifier": "865263040394502",
"RecordType": "D",
"TypeD_data": {
"tStat": {
"lDistance": 545190,
"lUpTimeGps": 21074,
"lUpTimeGsm": 34700,
"lUpTimeMCU": 127387,
"lUpTimeSys": 4727445,
"lUpTimeInMotion": 66343
},
"header": {
"info": 16797,
"tTime": 1597039293,
"reason": 4398046511136,
"version": 1
},
"tAcc_bas": {
"sXRaw": 65,
"sYRaw": 67,
"sZRaw": 983,
"Status": 1,
"sMagnitude": 66
},
"tAcc_ext": null,
"tGeo_bas": {
"polycount": 0
},
"tGeo_ext": null,
"tGps_bas": {
"Status": 11,
"sSpeed": 79,
"sCourse": 321,
"fLatitude": 48.59036,
"sAltitude": 474,
"fLongitude": 11.56852
},
"tGsm_bas": {
"ta": 0,
"mcc": 0,
"mnc": 0,
"Status": 2,
"cellcount": 0
},
"tGsm_ext": null,
"tTele_ext": null,
"tTele_int": {
"Status": 16,
"sTmpMCU": 2061,
"sBatLevel": 50,
"sBatVoltage": 3346,
"sExtVoltage": 0
},
"tTele_chain": null
},
"DataIdentifier": "Pos",
"Timestamp_Received": 1597039264
}
above there is the JSON I want to iterate through.
My Code:
public void SetValue(JObject value, string valueName = "")
{
foreach (var p in value)
{
if (p.Value is JObject)
{
SetValue((JObject)p.Value, valueName + "/" + p.Key);
}
}
}
above there is my Code.
Thank you in advance for the help.
public List<string> GetFieldNames(dynamic input)
{
List<string> fieldNames = new List<string>();
try
{
// Deserialize the input json string to an object
input = Newtonsoft.Json.JsonConvert.DeserializeObject(input);
// Json Object could either contain an array or an object or just values
// For the field names, navigate to the root or the first element
input = input.Root ?? input.First ?? input;
if (input != null)
{
// Get to the first element in the array
bool isArray = true;
while (isArray)
{
input = input.First ?? input;
if (input.GetType() == typeof(Newtonsoft.Json.Linq.JObject) ||
input.GetType() == typeof(Newtonsoft.Json.Linq.JValue) ||
input == null)
isArray = false;
}
// check if the object is of type JObject.
// If yes, read the properties of that JObject
if (input.GetType() == typeof(Newtonsoft.Json.Linq.JObject))
{
// Create JObject from object
Newtonsoft.Json.Linq.JObject inputJson =
Newtonsoft.Json.Linq.JObject.FromObject(input);
// Read Properties
var properties = inputJson.Properties();
// Loop through all the properties of that JObject
foreach (var property in properties)
{
// Check if there are any sub-fields (nested)
// i.e. the value of any field is another JObject or another JArray
if (property.Value.GetType() == typeof(Newtonsoft.Json.Linq.JObject) ||
property.Value.GetType() == typeof(Newtonsoft.Json.Linq.JArray))
{
// If yes, enter the recursive loop to extract sub-field names
var subFields = GetFieldNames(property.Value.ToString());
if (subFields != null && subFields.Count() > 0)
{
// join sub-field names with field name
//(e.g. Field1.SubField1, Field1.SubField2, etc.)
fieldNames.AddRange(
subFields
.Select(n =>
string.IsNullOrEmpty(n) ? property.Name :
string.Format("{0}.{1}", property.Name, n)));
}
}
else
{
// If there are no sub-fields, the property name is the field name
fieldNames.Add(property.Name + " : " + (string)property.Value<JProperty>());
}
}
}
else
if (input.GetType() == typeof(Newtonsoft.Json.Linq.JValue))
{
// for direct values, there is no field name
fieldNames.Add(string.Empty);
}
}
}
catch
{
throw;
}
return fieldNames;
}

Set value in JObject using JsonPath [duplicate]

There is a large JSON file (about a thousand lines). The task is to update existing JProperties, or add new JProperties in a specific location in the structure. The location of the new texts are based on the JToken.Path property. For example, this is the start of the JSON:
"JonSnow": {
"Direwolf": {
"Name": "Ghost",
"Color": "White",
}
}
"DanaerysTargaryen": {
"Dragons": {
"Dragon1": {
"Name": "Drogon",
}
}
"Hair": {
"Color": "White"
}
}
Now the JSON must be updated using a given list of JToken paths and the corresponding values.
The first possibility is, the JProperty corresponding to the path might already exist, in which case the value needs to be updated. I am already successfully implementing this with JToken.Replace().
The second possibility is, the JProperty does not exist yet and needs to be added. For example, I need to add "DanaerysTargaryen.Dragons.Dragon1.Color" with the value "Black".
I know I can use the JSON.Net Add() method, but to use this only the final child token of the path can be missing from the JSON. For example, I can use
JObject ObjToUpdate= JObject.Parse(jsonText);
JObject Dragon = ObjToUpdate["DanaerysTargaryen"]["Dragons"]["Dragon1"] as JObject;
Dragon.Add("Color", "Black"));
But what about if I need to add "JonSnow.Weapon.Type" with the value "Longsword"? Because "Weapon" does not exist yet as a JProperty, and it needs to be added along with "Type" : "Longsword". With each path, it is unknown how much of the path already exists in the JSON. How can this be parameterised?
// from outside source: Dictionary<string, string> PathBasedDict
// key: Jtoken.Path (example: "JonSnow.Weapon.Type")
// value: new text to be added (example: "Longsword")
foreach(KeyValuePair entry in PathBasedDict)
{
string path = entry.Key;
string newText = entry.Value;
if (ObjToUpdate.SelectToken(path) != null)
{ ObjToUpdate.SelectToken(path).Replace(newText); }
else AddToJson(path, newText);
}
What should AddToJson() look like? Iterating through the entire path and checking each possible JProperty to see if it exists, and then adding the rest underneath, seems very cumbersome. Is there a better way to do this? Any Json.NET tricks I am unaware of? I am not even sure how the iteration could be parameterised.
Based on first approach from Heretic Monkey's answer, here is an extension method:
public static class JObjectExtensions
{
/// <summary>
/// Replaces value based on path. New object tokens are created for missing parts of the given path.
/// </summary>
/// <param name="self">Instance to update</param>
/// <param name="path">Dot delimited path of the new value. E.g. 'foo.bar'</param>
/// <param name="value">Value to set.</param>
public static void ReplaceNested(this JObject self, string path, JToken value)
{
if (self is null)
throw new ArgumentNullException(nameof(self));
if (string.IsNullOrEmpty(path))
throw new ArgumentException("Path cannot be null or empty", nameof(path));
var pathParts = path.Split('.');
JToken currentNode = self;
for (int i = 0; i < pathParts.Length; i++)
{
var pathPart = pathParts[i];
var isLast = i == pathParts.Length - 1;
var partNode = currentNode.SelectToken(pathPart);
if (partNode is null)
{
var nodeToAdd = isLast ? value : new JObject();
((JObject)currentNode).Add(pathPart, nodeToAdd);
currentNode = currentNode.SelectToken(pathPart);
}
else
{
currentNode = partNode;
if (isLast)
currentNode.Replace(value);
}
}
}
}
There are a few ways of going about this. Here are two of them.
To go along with your existing code, split the path by '.', then iterate over them. If the path is not there, create it with Add. Otherwise, if we're on the last part of the path, just add the value.
var json = JObject.Parse(#"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toAdd = "DanaerysTargaryen.Dragons.Dragon1.Color";
var valueToAdd = "Black";
var pathParts = toAdd.Split('.');
JToken node = json;
for (int i = 0; i < pathParts.Length; i++)
{
var pathPart = pathParts[i];
var partNode = node.SelectToken(pathPart);
if (partNode == null && i < pathParts.Length - 1)
{
((JObject)node).Add(pathPart, new JObject());
partNode = node.SelectToken(pathPart);
}
else if (partNode == null && i == pathParts.Length - 1)
{
((JObject)node).Add(pathPart, valueToAdd);
partNode = node.SelectToken(pathPart);
}
node = partNode;
}
Console.WriteLine(json.ToString());
(Example on dotnetfiddle.net)
Otherwise, you could create a separate JObject that represents the node(s) you want to add, then merge them.
var json = JObject.Parse(#"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toMerge = #"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Color"":""Black""}}}}";
var jsonToMerge = JObject.Parse(toMerge);
json.Merge(jsonToMerge);
Console.WriteLine(json.ToString());
(Example on dotnetfiddle.net)

How to add a new JProperty to a JSON based on path?

There is a large JSON file (about a thousand lines). The task is to update existing JProperties, or add new JProperties in a specific location in the structure. The location of the new texts are based on the JToken.Path property. For example, this is the start of the JSON:
"JonSnow": {
"Direwolf": {
"Name": "Ghost",
"Color": "White",
}
}
"DanaerysTargaryen": {
"Dragons": {
"Dragon1": {
"Name": "Drogon",
}
}
"Hair": {
"Color": "White"
}
}
Now the JSON must be updated using a given list of JToken paths and the corresponding values.
The first possibility is, the JProperty corresponding to the path might already exist, in which case the value needs to be updated. I am already successfully implementing this with JToken.Replace().
The second possibility is, the JProperty does not exist yet and needs to be added. For example, I need to add "DanaerysTargaryen.Dragons.Dragon1.Color" with the value "Black".
I know I can use the JSON.Net Add() method, but to use this only the final child token of the path can be missing from the JSON. For example, I can use
JObject ObjToUpdate= JObject.Parse(jsonText);
JObject Dragon = ObjToUpdate["DanaerysTargaryen"]["Dragons"]["Dragon1"] as JObject;
Dragon.Add("Color", "Black"));
But what about if I need to add "JonSnow.Weapon.Type" with the value "Longsword"? Because "Weapon" does not exist yet as a JProperty, and it needs to be added along with "Type" : "Longsword". With each path, it is unknown how much of the path already exists in the JSON. How can this be parameterised?
// from outside source: Dictionary<string, string> PathBasedDict
// key: Jtoken.Path (example: "JonSnow.Weapon.Type")
// value: new text to be added (example: "Longsword")
foreach(KeyValuePair entry in PathBasedDict)
{
string path = entry.Key;
string newText = entry.Value;
if (ObjToUpdate.SelectToken(path) != null)
{ ObjToUpdate.SelectToken(path).Replace(newText); }
else AddToJson(path, newText);
}
What should AddToJson() look like? Iterating through the entire path and checking each possible JProperty to see if it exists, and then adding the rest underneath, seems very cumbersome. Is there a better way to do this? Any Json.NET tricks I am unaware of? I am not even sure how the iteration could be parameterised.
Based on first approach from Heretic Monkey's answer, here is an extension method:
public static class JObjectExtensions
{
/// <summary>
/// Replaces value based on path. New object tokens are created for missing parts of the given path.
/// </summary>
/// <param name="self">Instance to update</param>
/// <param name="path">Dot delimited path of the new value. E.g. 'foo.bar'</param>
/// <param name="value">Value to set.</param>
public static void ReplaceNested(this JObject self, string path, JToken value)
{
if (self is null)
throw new ArgumentNullException(nameof(self));
if (string.IsNullOrEmpty(path))
throw new ArgumentException("Path cannot be null or empty", nameof(path));
var pathParts = path.Split('.');
JToken currentNode = self;
for (int i = 0; i < pathParts.Length; i++)
{
var pathPart = pathParts[i];
var isLast = i == pathParts.Length - 1;
var partNode = currentNode.SelectToken(pathPart);
if (partNode is null)
{
var nodeToAdd = isLast ? value : new JObject();
((JObject)currentNode).Add(pathPart, nodeToAdd);
currentNode = currentNode.SelectToken(pathPart);
}
else
{
currentNode = partNode;
if (isLast)
currentNode.Replace(value);
}
}
}
}
There are a few ways of going about this. Here are two of them.
To go along with your existing code, split the path by '.', then iterate over them. If the path is not there, create it with Add. Otherwise, if we're on the last part of the path, just add the value.
var json = JObject.Parse(#"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toAdd = "DanaerysTargaryen.Dragons.Dragon1.Color";
var valueToAdd = "Black";
var pathParts = toAdd.Split('.');
JToken node = json;
for (int i = 0; i < pathParts.Length; i++)
{
var pathPart = pathParts[i];
var partNode = node.SelectToken(pathPart);
if (partNode == null && i < pathParts.Length - 1)
{
((JObject)node).Add(pathPart, new JObject());
partNode = node.SelectToken(pathPart);
}
else if (partNode == null && i == pathParts.Length - 1)
{
((JObject)node).Add(pathPart, valueToAdd);
partNode = node.SelectToken(pathPart);
}
node = partNode;
}
Console.WriteLine(json.ToString());
(Example on dotnetfiddle.net)
Otherwise, you could create a separate JObject that represents the node(s) you want to add, then merge them.
var json = JObject.Parse(#"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Name"": ""Drogon""}},""Hair"": {""Color"": ""White""}}}");
var toMerge = #"{""DanaerysTargaryen"":{""Dragons"":{""Dragon1"":{""Color"":""Black""}}}}";
var jsonToMerge = JObject.Parse(toMerge);
json.Merge(jsonToMerge);
Console.WriteLine(json.ToString());
(Example on dotnetfiddle.net)

Identifying that a property's value is an array

I have a JSON file:
{
"abn":"63119059513",
"acn":"119059513",
"business_structure":"Private Company",
"ngr_number":"1231231",
"cbh_number":"1231231",
"main_name":"Brickworks Building Products Pty Ltd",
"trading_name":"Brickworks",
"other_trading_names":"Austral Bricks",
"directors":[
{
"ID":"12114",
"ae_forms_filled_in_ID":"22739",
"name":"John Smith",
"dob":"1983-10-29",
"address_line_1":"123 Fake Street",
"address_line_2":"",
"address_line_city":"Fakeland",
"address_line_postcode":"2000",
"address_line_state":"New South Wales",
"address_line_country":"Australia",
"order_extract_id":null,
"director_found":null,
"drivers_lic":"",
"home_mortgage":"",
"phone":"",
"mobile":"",
"director_email":"",
"director_title":"Mr",
"director_position":"Director",
"dir_pdf_url":null
}
],
}
I want to determine if the value of any property has a structure of an array. The best I can come up with so far is:
StreamReader streamrr = new StreamReader("C:\\temp\\agfarm_example_udate.json", Encoding.UTF8);
string JSON = streamrr.ReadToEnd();
JObject CWFile = JObject.Parse(JSON);
foreach (JProperty property in CWFile.Properties())
{
// Do something
if (property.Value.ToString().Contains("["))
{
// Do something with the array
JArray items = (JArray)CWFile[property.Name];
foreach (JObject o in items.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
// Do something
}
}
}
}
To determine whether or not a property value has an array, I used the condition:
if (property.Value.ToString().Contains("["))
I'm just wondering if there is a better way of doing this check?
One way to do this is to check the JToken.Type property. Arrays are of type JTokenType.Array:
if (property.Value.Type == JTokenType.Array)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Or, you can just try to cast to JArray:
if (property.Value is JArray)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Both are preferable to checking property.Value.ToString().Contains("[") since a nested property might have an array value, thus causing a bracket to appear somewhere in the ToString() return.
If you want to recursively find every property with an array value, you can introduce an extension method:
public static class JsonExtensions
{
public static IEnumerable<JToken> WalkTokens(this JToken node)
{
if (node == null)
yield break;
yield return node;
foreach (var child in node.Children())
foreach (var childNode in child.WalkTokens())
yield return childNode;
}
}
And then do:
var CWFile = JToken.Parse(JSON)
var arrayProperties = CWFile.WalkTokens().OfType<JProperty>().Where(prop => prop.Value.Type == JTokenType.Array);
public void Traverse(string name, JToken j)
{
foreach (JToken token in j.AsJEnumerable())
if (token.Type == JTokenType.Object)
{
foreach (var pair in token as JObject)
{
string name_ = pair.Key;
JToken child = pair.Value;
Traverse(name, child);
}
}
else if (token.Type == JTokenType.Array) //an array property found
{
foreach (var child in token.Children())
Traverse(((JProperty)j).Name, child);
}
else if (token.Type == JTokenType.Property)
{
var property = token as JProperty; //current level property
Traverse(name, (JContainer)token);
}
else //current level property name & value
{
var nm = "";
var t = "";
if (j is JProperty)
{
nm = ((JProperty)j).Name;
t = Convert.ToString(((JProperty)j).Value);
}
t = Convert.ToString(token);
}
}
Call:
JSON = JObject.Parse(...);
Traverse("", JSON);
To parse and already parsed text for the same reason is not very wise :
if (property.Value.ToString().Contains("["))
Json structure is simple : objects, arrays, properties, values
http://www.json.org/
So array is a wellknown object for json and we are looking for it :
if (token.Type == JTokenType.Array) //an array property

Categories