C# dynamic object, modify properties based on string paths - c#

The use case is pretty simple in concept. I receive a json payload that has two properties on the root level:
instructions
base
Instructions are set of instructions that I am supposed to apply on the base json object.
For eg - according to the below payload,
I am supposed to traverse to the widgets within defaultWidgets of the base property.
Then replace it completely with whatever is the value of patchedValue.
Input Payload:
{
"instructions": [
{
"patchedPath": "defaultWidget.widgets",
"patchedValue": false,
}
],
"base": {
"defaultWidget": {
"hash": "ktocle2l0u527",
"layout": "6|6",
"managerId": "defaultWidget",
"widgets": [
{
"managerId": "defaultWidget",
"widgetId": "invCreateWid7",
"type": "standard",
"manifestPath": "nexxe.standard-section#0.0.0-next.11",
"defaultInputManifestPath": "nexxe.input#0.0.1-alpha.49",
"title": "scannedInvoice",
"children": [
{
"name": "tom"
}
],
"hash": "ktocle2lrgps9",
"directives": ""
}
]
}
}
}
The result should be :
{
"base": {
"defaultWidget": {
"hash": "ktocle2l0u527",
"layout": "6|6",
"managerId": "defaultWidget",
"widgets": false
}
}
}
Code:
var stringPayload = "{ \"instructions\": [ { \"patchedPath\": \"defaultWidget.widgets\", \"patchedValue\": false, } ], \"base\": { \"defaultWidget\": { \"hash\": \"ktocle2l0u527\", \"layout\": \"6|6\", \"managerId\": \"defaultWidget\", \"widgets\": [ { \"managerId\": \"defaultWidget\", \"widgetId\": \"invCreateWid7\", \"type\": \"standard\", \"manifestPath\": \"nexxe.standard-section#0.0.0-next.11\", \"defaultInputManifestPath\": \"nexxe.input#0.0.1-alpha.49\", \"title\": \"scannedInvoice\", \"children\": [ { \"name\": \"tom\" } ], \"hash\": \"ktocle2lrgps9\", \"directives\": \"\" } ] } }}";
var parsedPayload = JsonConvert.DeserializeObject(stringPayload);
var baseJ = parsedPayload.GetType().GetProperty("instructions").GetValue(parsedPayload, null);
string jsonString = JsonConvert.SerializeObject(parsedPayload);
I am stuck on the very initial steps , I am getting:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
System.Type.GetProperty(...) returned null.
This is what QuickWatch says:

What's returned by DeserializeObject in this case is JObject so to start with you can cast to it:
var parsedPayload = (JObject) JsonConvert.DeserializeObject(stringPayload);
Then grab instructions and target to change:
var instructions = (JArray) parsedPayload["instructions"]; // cast to JArray
var result = parsedPayload["base"];
Then we can go over instructions and apply them:
foreach (var instruction in instructions) {
// grab target path and value
var targetPath = (string) ((JValue)instruction["patchedPath"]).Value;
var targetValue = (JValue)instruction["patchedValue"];
// temp variable to traverse the path
var target = result;
foreach (var part in targetPath.Split('.')) {
target = target[part];
}
// replace the value
target.Replace(targetValue);
}
Now result contains what was in base with instructions applied.

With Json.NET you can do that like this:
var json = File.ReadAllText("sample.json");
var semiParsedJson = JObject.Parse(json);
var instructions = (JArray)semiParsedJson["instructions"];
var #base = semiParsedJson["base"];
foreach (var instruction in instructions)
{
var path = (string)instruction["patchedPath"];
var newValue = (string)instruction["patchedValue"];
var toBeReplaced = #base.SelectToken(path);
toBeReplaced.Replace(newValue);
}
JObject.Parse parses the json string
With the index operator [] we retrieve the two top level nodes. - One of them is an array (that's why there is an explicit JArray cast)
The other one is a JToken
We iterate through the array and retrieve the path and the newvalue
We use the SelectToken to get the desired node and then apply the replacement via the Replace method.
Please bear in mind that this solution is not bulletproof. You might need to change the indexer operator to TryGetValue to be able to check existence before you perform any operation on the JToken.
You also need to check that the patchedPath is valid at all.

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;

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

I can´t find the track and trace number in my json string

I can´t find a value in a json string using json.net
I´ve tried jsonstr[0].track_numbers[0].track_number
This is my json file.
{
"0": {
"increment_id": "112",
"track_numbers": [
{
"track_number": "2223",
"title": "tit",
"carrier_code": "custom"
}
]
},
"live_shipping_status": "Delivered"
}
I want to find the Track_nummber.
dynamic jsonstr = JsonConvert.DeserializeObject(json));
var track = jsonstr[0].track_numbers[0].track_number
(donsent work)
The 0 of your json is a string key, not an index position:
dynamic obj = JsonConvert.DeserializeObject(json);
var trackNumber = obj["0"].track_numbers[0].track_number;
Note the difference in getting the first entry of track_numbers, which is an array.

Converting "[myObject][1][application]" string to useable path on JObject

First let me start off by showing you what I am trying to accomplish by using pseudo code. (Yes I know this example is not possible)
var path = "[DocInfo][Type]";
var doc = couchDbCall();
var specificProperty = doc[path];
I want to turn a string into a literaly path accessor in code but cannot seem to find any examples etc; probably due to my lack of a good search query.
{
"_id": "bb9f9e13-218a-4403-9920-79b0d353634b",
"_rev": "2-908d1257d7324a8b6f1c333cbefe2e41",
"Name": "conversionTest2",
"DocInfo": {
"Type": "Application",
"Platform": "test",
"Version": "12",
"VersionRelId": -1,
"Category": "Communications and Messaging",
"Created": {
"CreatedDate": "3/19/2015 7:56:07 PM",
"User": {
"Id": "bf51f6ce-d3f7-46ff-9ff0-c1e60c2ded44",
"Name": "Adrian Campos",
"Email": "adrian.campos#bizdox.com"
}
},
"Template": "Other Application",
"TemplateVersion": 0
}
}
Example with JSON.Net (get it via NuGet):
dynamic o = JsonConvert.DeserializeObject(yourJsonString);
string type = o.DocInfo.Type; // gets "Application"
var doc = couchDbCall();
var specificProperty = doc[type]; // assuming doc takes a string as key
UPDATE:
that works also:
string type = o["DocInfo"]["Type"];
UPDATE 2:
as far as I understand this little recursive method will help you out (note that there's no error handling whatsoever):
private string DoMyPaths(List<string> paths, JToken token)
{
string s = paths[0];
if (paths.Count > 1)
{
paths.RemoveAt(0);
JToken result = token[s];
return DoMyPaths(paths, result);
}
return token[s].ToString();
}
now you can pass in your path (delimited by '.'):
string myPath = "DocInfo.Created.User.Name";
var paths = myPath.Split('.').ToList();
dynamic o = JsonConvert.DeserializeObject(json);
var result = DoMyPaths(paths, o);
result:
"Adrian Campos"

Convert JSON String to JSON Object c#

I have this String stored in my database:
str = "{ "context_name": { "lower_bound": "value", "upper_bound": "value", "values": [ "value1", "valueN" ] } }"
This string is already in the JSON format but I want to convert it into a JObject or JSON Object.
JObject json = new JObject();
I tried the json = (JObject)str; cast but it didn't work so how can I do it?
JObject defines method Parse for this:
JObject json = JObject.Parse(str);
You might want to refer to Json.NET documentation.
if you don't want or need a typed object try:
using Newtonsoft.Json;
// ...
dynamic json = JsonConvert.DeserializeObject(str);
or try for a typed object try:
using Newtonsoft.Json;
// single
Foo foo = JsonConvert.DeserializeObject<Foo>(str);
// or as a list
List<Foo> foos = JsonConvert.DeserializeObject<List<Foo>>(str);
This works
string str = "{ 'context_name': { 'lower_bound': 'value', 'pper_bound': 'value', 'values': [ 'value1', 'valueN' ] } }";
JavaScriptSerializer j = new JavaScriptSerializer();
object a = j.Deserialize(str, typeof(object));
there's an interesting way to achive another goal which is to have a strongly type class base on json with a very powerfull tools that i used few days ago for first time to translate tradedoubler json result into classes
Is a simple tool: copy your json source paste and in few second you will have a strongly typed class json oriented .
In this manner you will use these classes which is more powerful and simply to use.
You can try like following:
string output = JsonConvert.SerializeObject(jsonStr);
This works for me using JsonConvert
var result = JsonConvert.DeserializeObject<Class>(responseString);
If your JSon string has "" double quote instead of a single quote ' and has \n as a indicator of a next line then you need to remove it because that's not a proper JSon string, example as shown below:
SomeClass dna = new SomeClass ();
string response = wc.DownloadString(url);
string strRemSlash = response.Replace("\"", "\'");
string strRemNline = strRemSlash.Replace("\n", " ");
// Time to desrialize it to convert it into an object class.
dna = JsonConvert.DeserializeObject<SomeClass>(#strRemNline);
In a situation where you are retrieving a list of objects of a certain entity from your api, your response string may look like this:
[{"id":1,"nome":"eeee","username":null,"email":null},{"id":2,"nome":"eeee","username":null,"email":null},{"id":3,"nome":"Ricardo","username":null,"email":null}]
In this situation you may want an array of Jason objects and cycle through them to populate your c# variable. I've done like so:
var httpResponse = await Http.GetAsync($"api/{entidadeSelecionada}");
List<List<string[]>> Valores = new();
if (httpResponse.IsSuccessStatusCode)
{
//totalPagesQuantity = int.Parse(httpResponse.Headers.GetValues("pagesQuantity").FirstOrDefault());
//Aqui tenho que colocar um try para o caso de ser retornado um objecto vazio
var responseString = await httpResponse.Content.ReadAsStringAsync();
JArray array = JArray.Parse(responseString);
foreach (JObject objx in array.Children<JObject>())
{
List<string[]> ls = new();
foreach (JProperty singleProp in objx.Properties())
{
if (!singleProp.Name.Contains("_xyz"))
{
string[] val = new string[2];
val[0] = singleProp.Name;
val[1] = singleProp.Value.ToString();
ls.Add(val);
}
}
Valores.Add(ls);
}
}
return Valores;
I achieved this solution by the #Andrei answer.
This does't work in case of the JObject this works for the simple json format data. I have tried my data of the below json format data to deserialize in the type but didn't get the response.
For this Json
{
"Customer": {
"id": "Shell",
"Installations": [
{
"id": "Shell.Bangalore",
"Stations": [
{
"id": "Shell.Bangalore.BTM",
"Pumps": [
{
"id": "Shell.Bangalore.BTM.pump1"
},
{
"id": "Shell.Bangalore.BTM.pump2"
},
{
"id": "Shell.Bangalore.BTM.pump3"
}
]
},
{
"id": "Shell.Bangalore.Madiwala",
"Pumps": [
{
"id": "Shell.Bangalore.Madiwala.pump4"
},
{
"id": "Shell.Bangalore.Madiwala.pump5"
}
]
}
]
}
]
}
}
string result = await resp.Content.ReadAsStringAsync();
List<ListView11> _Resp = JsonConvert.DeserializeObject<List<ListView11>>(result);
//List<ListView11> _objList = new List<ListView11>((IEnumerable<ListView11>)_Resp);
IList usll = _Resp.Select(a => a.lttsdata).ToList();
// List<ListViewClass> _objList = new List<ListViewClass>((IEnumerable<ListViewClass>)_Resp);
//IList usll = _objList.OrderBy(a=> a.ReqID).ToList();
Lv.ItemsSource = usll;

Categories