Selecting value from a JSON string - c#

I am having a heck of a time getting a particular value from json and am hoping someone can help. The json I'm using comes from a form post, so will typically be different each time, but the IDs will always be the same (just like every form!)...and I do not have control over the format of the json. I am able to get the name value from this just fine (using Newtonsoft.Json), but am stuck on how to get the "More then 5 years" value...
Here is what I am using to get the "My name is" value, which (obviously) doesn't work for "Years of Employment".
C#:
obj["form_response"]["answers"].SelectToken("$.[?(#field.id == '26')]..text").ToString();
JSON:
{
"event_type":"form_response",
"form_response":{
"definition":{
"id":"c33500e5",
"title":"Employee Satisfaction",
"fields":[
{
"id":"33",
"title":"Years of employment:",
"type":"multiple_choice"
},
{
"id":"26",
"title":"My name is:",
"type":"short_text"
}
]
},
"answers":[
{
"type":"choice",
"choice":{
"label":"More than 5 years"
},
"field":{
"id":"33",
"type":"multiple_choice"
}
},
{
"type":"text",
"text":"Bill Dunn",
"field":{
"id":"26",
"type":"short_text"
}
}
]
}
}

The value you want is nested as follows: { "choice": { "label": "More than 5 years", ... } } so you can do:
var result = obj["form_response"]["answers"].SelectToken("$.[?(#field.id == '33')].choice.label");
var resultJson = result.ToString();

Related

API return to my angular app 2 employee identic

My API returns this JSON, it's the same employee which is present 2 times in my database.
I can't delete it in my database, also how can I print only 1 of them when I make my get in angular?
Or when I make my *ngfor:"let e of employee", how can I print only the first result?
getEmployee(id: string){
return this.http.get<any>(this.localUrlAPI+"/employee/getEmployee/"+id)
.pipe(map((res:any)=>{
return res.Data;
}))
}
{
"Data":[
{
"IdEmployee": "1",
"Name": "Jacob"
}
{
"IdEmployee": "1",
"Name" ; "Jacob"
}
]
}
so you want to remove duplicates, there are many approaches, this is one:
.pipe(map((res:any)=>{
const uniqueData = new Map<number, any>();
for (const data of res.Data) {
uniqueData.set(data.id, data);
}
return Array.from(uniqueData.values());
}))

Flexibly edit json values in C#

I've been trying to insert values from one json file into another. These structures have mostly been decided on, so I want to try and make them work as they are.
The problem i've been running into is that the provided paths may not always have the same depth and thus the coded used to insert values into that provided path needs to be flexible enough to deal with that.
These are the json files:
this json holds values, the paths to where those values should be placed in the other json file and when applicable, the index.
[
{
"path": "x_axis.data",
"value": "['','2005','2010','2015','2021','2026']",
"index": null
},
{
"path": "series.data",
"value": "[77.0,70.0,74.0,0.0,0.0,0.0]",
"index": 0
},
{
"path": "series.data",
"value": "[0.0,0.0,0.0,99.0,100.0,100.0]",
"index": 1
}
]
This json is a template structure that will be reused. it' based on the Echarts documentation, since we use that library to show charts.
{
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {
"data": [ "One", "Two" ]
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": [
{
"type": "category",
"data": []
}
],
"yAxis": [
{
"type": "value"
}
],
"series": [
{
"name": "One%",
"type": "bar",
"stack": "Stack 1",
"emphasis": {
"focus": "series"
},
"itemStyle": {
"color": "blue"
},
"data": []
},
{
"name": "Two%",
"type": "bar",
"stack": "Stack 1",
"emphasis": {
"focus": "series"
},
"itemStyle": {
"color": "red"
},
"data": []
}
]
}
Currently my method converts the path into a string array with text formatting that matches the names in the template json. I am however unsure how to use it or whether it was the right choice.
Here is my code:
public static string GenerateChart(string templateString, string replacementValuesString)
{
string result = "";
dynamic templateJson = JObject.Parse(templateString);
dynamic replacementJson = JArray.Parse(replacementValuesString);
foreach (var item in replacementJson)
{
string[] pathArr = CreatePathArray(item);
//check for index
if (item.index != null)
{
//TODO: Implement code that handles the index
templateJson.path = item.value;
}
else
{
templateJson.path = item.value;
}
}
result = templateJson.ToString();
return result;
}
I used Newtonsoft to parse the json. My team decided against using models to deserialise the Json because they wanted to be able to only edit the json files for maintenance of the program.
My question to anyone willing to help is how would you go about solving this problem?
If I've failed to explain bits properly please let me know so I can clarify.
Thank you for your help!
Edit 1: I forgot to mention how the path is split into the array. It's split at the period, so "x_axis.data" becomes "["xAxis", "data"]".
Thanks to E. Shcherbo for helping me find a solution
I ended up modifying the code he suggested to fit my project as a few minor things were causing issues. Here's the code I ended up using and will now continue to polish:
var replacementJson = JArray.Parse(replacementValuesString);
JToken templateJson = JObject.Parse(templateString);
foreach (var item in replacementJson)
{
string[] pathArr = CreatePathArray(item);
//The method using ^1 provided by E. Shcherbo didn't work for me here.
//I'm guessing it's because I'm working in an old version of dotnetframework.
var valuePropName = pathArr[pathArr.Length - 1];
//Using this loop to "dive deeply" into the template json as E. put it, we find the correct path and then modify it.
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson == null)
{
Console.WriteLine("Template path given is invalid.");
break;
}
}
// if my item contains a value for index, it will be used. Otherwise it's safe to assume we're not dealing with an array.
if (item["index"] != null)
{
UpdateValueInTemplate(templateJson, item["index"], valuePropName, item["value"]);
}
else
{
UpdateValueInTemplate(templateJson, valuePropName, item["value"]);
}
templateJson = templateJson.Root;
}
First of all you probably don't need dynamic type here, because you know types of your objects at compile time JObject, JArray, JToken, so this is much safer to use them (you can go with var).
I'll describe two approaches I see here (both of them reuse the UpdateValueInTemplate method).
Note: be careful with different edge cases which might be possible depending on what you can and can't assume about your data.
Giving your approach with an array which represents the path, you can just iterate through the array and go deeply and deeply in your templateJson. Something like this
JToken templateJson = JObject.Parse(templateString);
var valuePropName = pathArr[^1]; // ^1 means first from the end
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson is null)
// The path is wrong. Do whatever validation is expected
}
UpdateValueInTemplate(templateJson, valuePropName, item["value"]);
You can also pass your path from replacementJson (without the last property to get an object where the last property is rather than value of the property) into the SelectToken method. Something like this:
var path = item["path"].ToString();
var lastPathSplitter = item.Path.LastIndexOf('.');
var pathWithoutLastProperty = path.Substring(0, lastPathSplitter);
var valPropName = item.Path.Substring(lastPathSplitter + 1);
var jsonToUpdate = templateJson.SelectToken(pathWithoutLastProperty);
if (jsonToUpdate is null)
// The path is wrong. Do whatever validation is expected
UpdateValueInTemplate(jsonToUpdate, valPropName, item["value"]);
Then the UpdateValueInTemplate method can look like this:
void UpdateValueInTemplate(JToken tokenToUpdate, string valPropName, JToken value) {
tokenToUpdate[valPropName] = value;
}
You can also reduce manipulations with your path if you pass the entire path to the SelectToken and then will use yourToken.Parent.Parent[valPropName] = value to update the json.
UPDATE
I didn't notice that you also want to handle cases where the first property of the path can point to the array. So when this is the case it can be handled like this:
JToken templateJson = JObject.Parse(templateString);
var valuePropName = pathArr[^1]; // ^1 means first from the end
for (int i = 0; i < pathArr.Length - 1; i++)
{
var pathKey = pathArr[i];
templateJson = templateJson[pathKey];
if (templateJson is null)
{
// The path is wrong. Do whatever validation is expected
}
else if (templateJson.Type == JTokenType.Array)
{
int index = item["index"].ToObject<int>(); // you may want to validate the index presence and format
templateJson = templateJson[index];
}
}
For approach with SelectToken you can either change your replacement json to match the format SelectToken understands or create a new path based on the index when it's not null:
var firstPropSplitter = path.IndexOf('.');
var firstProperty = path.Substring(0, firstPropSplitter);
var restPath = path.Substring(firstPropSplitter + 1);
var index = item["index"];
var newPath = $"{firstProperty}[{index}].{restPath}";

Json Object parse error

I am storing data in my cassandra database as string and i want to retrieve the string and convert it into json. i get an exception saying
An unhandled exception of type 'Newtonsoft.Json.JsonReaderException' happened
is there something wrong with the data stored ? or i am doing some other thing wrong ?
My query is :
INSERT INTO table2(key1,col1) values ('5',{'url':'{"hello":
{"hi":{"hey":"1","time":"5"}},
{"reg":{"hey":"1","time":"1"}},
{"install":{"hey":"0"}}},
"task":"1","retry":"00",
"max":"5","call":"140"'});
In my db when i click the map<text,text> column, it gets stored as :
{\"hello\":\r\n
{\"subscription_atrisk\":{\"hey\":\"1\",\"time\":\"100\"}},
{\"reg\":{\"hey\":\"1\",\"time\":\"2000\"}},
\"task\":\"0\",\"retry\":\"300\",\"max\":\"5\",\"call\":\"14400\"}
in my c# code, i try
string s = "{\"hello\":\r\n {\"subscription_atrisk\":{\"hey\":\"1\",\"time\":\"100\"}},{\"reg\":{\"hey\":\"1\",\"time\":\"2000\"}},\"task\":\"0\",\"retry\":\"300\",\"max\":\"5\",\"call\":\"14400\"";
Jobject json = Jobject.Parse(s); //Get an Error here.
Could anyone please throw some light on this ?
In JSON, an object should contain a key and either a value or another object/array.
You have syntax errors in your JSON object. First of all, always use double quotes.
Then... The url object has a key Hello but then where you are supposed to place two more keys, you're just putting there two more objects, as if it was an array.
This could be a correct syntax:
{
"url": {
"hello": {
"hi": {
"hey": "1",
"time": "5"
}
},
"MissingKey1":{
"reg": {
"hey": "1",
"time": "1"
}
},
"MissingKey2":{
"install": {
"hey": "0"
}
}
},
"task": "1",
"retry": "00",
"max": "5",
"call": "140"
}
Or if you really meant to have a hello object and an array of two more objects inside url:
{
"url": {
"hello": {
"hi": {
"hey": "1",
"time": "5"
}
},
"AnArray": [{
"reg": {
"hey": "1",
"time": "1"
}
}, {
"install": {
"hey": "0"
}
}]
},
"task": "1",
"retry": "00",
"max": "5",
"call": "140"
}
I suggest that before you store those JSON objects in the database, ALWAYS validate them to make sure that they're valid of syntax.
Or even better: this Newtonsoft library provides functions which serializes(creates) JSON strings from other objects. See JsonConvert.SerializeObject Method
In general it is a good idea to look around in the API documentation, it really can save you a lot of time so that you don't have to do unnecessary work.
Looks like you're missing a closing }
Try:
string s = "{\"hello\": {\"subscription_atrisk\":{\"hey\":\"1\",\"time\":\"100\"}}, \"missing_key\": {\"reg\":{\"hey\":\"1\",\"time\":\"2000\"}},\"task\":\"0\",\"retry\":\"300\",\"max\":\"5\",\"call\":\"14400\"}";

Json.NET SelectTokens not working if json contains empty array

I have a valid JSON object that contains multiple "en-US" keys, which I'm trying select. For that purpose I use the JsonPath
"$..en-US"
which is given to the SelectTokens procedure implemented by the Json.NET. It's a JSON framework for .NET . Everything is working fine and well as long as my JSON doesn't contain any empty array.
Here's an example:
var myJsonPath = "$..en-US";
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'header': {
'controls': []
},
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
var jToken = JObject.Parse(myJson);
var tokens = jToken.SelectTokens(myJsonPath);
Here, the tokens variable will contain just one element! That will be the "en-US" occurence BEFORE the empty array in the 'controls' of the 'header' object. However, when I just leave this 'header' object out:
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
I will get all the 3 occurencies of the "en-US" as expected. Btw, if I validate my JsonPath on the first JSON object (i.e. which contains an empty array) in an online tool, then as expected, I get all the three "en-US" cases. This diverges from what I'm getting from the Json.NET. I'm wondering whether it's a bug or do I have to handle this case manually somehow?
This is a bug that has been fixed. Upgrade to the latest version of Json.NET.
If you're in the same situation as me where you're a bit stuck with respect to updating your version of Json.NET, you can work around the issue by doing something along the lines of this:
IEnumerable<JValue> vals = jToken
.Desecendants()
.Where(w => w is JProperty && w.Name=="en-US")
.Select(s => s.Value);
Hope that helps! The vals array will contain the same tokens you would have gotten using the selector you were trying to use before.

JsonConvert unexpected behavior when serialising Dictionary

Hell all! I have a Dictionary<string,Dictionary<CustomClass,string>> that i want to serialise.
The result I expect is something like:
{
"key1":{
{
"CustomClassProperty1":"val1",
"CustomClassProperty2":"val2",
"CustomClassProperty3":"val3"
}:"Final STR",
{
"CustomClassProperty1":"val10",
"CustomClassProperty2":"val2",
"CustomClassProperty3":"val35"
}:"Final STR4",
{
"CustomClassProperty1":"val100",
"CustomClassProperty2":"val25",
"CustomClassProperty3":"val300"
}:"Final STR8"
},
"key2":{
{
"CustomClassProperty1":"val4",
"CustomClassProperty2":"val5",
"CustomClassProperty3":"val6"
}:"Final STR 2"
},
"key3":{
{
"CustomClassProperty1":"val1",
"CustomClassProperty2":"val7",
"CustomClassProperty3":"val5"
}:"Final STR 3",
{
"CustomClassProperty1":"val10",
"CustomClassProperty2":"val2",
"CustomClassProperty3":"val35"
}:"Final STR0",
{
"CustomClassProperty1":"val100",
"CustomClassProperty2":"val25",
"CustomClassProperty3":"val300"
}:"Final STR10"
}
}
But instead i'm getting
{
"key1":{
"MyProjectNamespace.CustomClass":"Final STR",
"MyProjectNamespace.CustomClass":"Final STR4"
},
"key2":{
"MyProjectNamespace.CustomClass":"Final STR 2"
},
"key3":{
"MyProjectNamespace.CustomClass":"Final STR 3"
}
}
Can anyone tell me how to make it right? I dont want the "namespace.classname" but the properties... I`m using Newtonsoft.Json btw... tks a lot!
Your desired output isn't in JSON format. The left hand side of the : is the name of the object property, and must be a string. To get the format you've listed, you'll need to use another serializer that supports your non-JSON format.

Categories

Resources