Adding a complex object to JObject using a delimited path/key - c#

I am working with Jsons which I don't know their structure in advanced. Just for example:
{
"OrganizationData": {
"Org1": {
"Name": "Rega And Dodli",
"EmployessNum": "100000000"
},
"Org2": {
"Name": "Sami And Soso",
"EmployessNum": "2"
}
}
}
I'm currently getting values by using the SelectToken method to which I can pass a key with a sub key like this:
var token = myJObject.SelectToken("OrganizationData.Org1")
This works fine. Now I want to add a new entry to the JSON using a string like that, something like:
myJObject.Add("OrganizationData.Org3", myValueJson);
but calling add like that directly just adds a new key to the json called "OrganizationData.Org3" and not creating a new sub key called "Org3" inside "OrganizationData" like the current "Org1" and "Org2".
How can I add a new value with a delimited string like needed?

JSON doesn't have subkeys or delimited keys. OrganizationData.Org1 is a LINQ to JSON search expression, not a subkey.
To add Org3 you can use one of the many ways available to modify a JSON object. You can add a child element to OrganizationData or a sibling to one of the other Org nodes.
To add a child element to a node, you could use .SelectToken("OrganizationData") if you don't already have a reference to it, and use JObject.Add to add the new node. You'll have to cast the result to JObject first, as SelectToken returns a JToken. If there's a chance that OrganizationData is an array, you'll have to check the type too.
For example:
var token = myJObject.SelectToken("OrganizationData");
if(token is JObject orgObj)
{
orgObj.Add("Org3",myValueJson);
}
Working with unknown paths
The same thing works if the path is specified at runtime. In this case, all that's needed is to separate the last part from the rest of the path, perhaps using String.LastIndexOf`:
var lastDot=path.LastIndexOf('.');
if (lastDot<0)
{
//Oops! There's no dot. What do we do now?
}
var parent=path.Substring(0,lastDot);
var key=path.Substring(lastDot+1);
var token = myJObject.SelectToken(parent);
if(token is JObject orgObj)
{
orgObj.Add(key,myValueJson);
}
You'll have to decide what to do if the path contains no dot. Is this an invalid path? Or should a new object be added under the root object?

Related

Getting error Newtonsoft.Json.Linq.JProperty cannot have multiple values when adding JToken

I have the following structure of additional information where I need to update the value of one of the tokens in the structure. The data is an array of JTokens with a parent called 'additionalFields' as follows:
{{"additionalFields":
[
{ "name": "NAME1", "value": "VALUE1" },
{ "name": "NAME2", "value": "VALUE2" },
{ "name": "NAME3", "value": "VALUE3" },
{ "name": "NAME4", "value": "VALUE4" }
]}
I'm trying to update the value of one of the tokens e.g. to change VALUE1 to VALUE10.
Once I have located the token I need to update my code removes it as follows.
additionalField.Remove();
I then create a new token to replace the one I have removed (containing the new value) using the following functions.
public static JToken CreateNewToken(string name, string value)
{
var stringToken = CreateNewStringToken(name, value);
var token = JToken.Parse(stringToken);
return (JToken) token;
}
private static string CreateNewStringToken(string name, string value)
{
return $"{{\"name\":\"{name}\",\"value\":\"{value}\"}}";
}
I then add the new token as follows.
additionalFields.AddAfterSelf(updatedToken);
Putting it all together we have the following
foreach (var additionalField in additionalFields)
{
//is this the key we are looking for?
var keyToken = additionalField.First;
if (keyToken?.First == null) continue;
if (string.Equals(keyToken.First.ToString(), "newname", StringComparison.CurrentCultureIgnoreCase))
{
//remove the current token
additionalField.Remove();
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalFields.AddAfterSelf(updatedToken); <-- error occurs here!!
}
}
However after adding the token I'm getting the following error
Newtonsoft.Json.Linq.JProperty cannot have multiple values
I can see in the debugger that the token has been removed (as the token.Count is reduced by 1) so cannot understand why I'm getting an error adding the replacement token.
I was able to reproduce your problem here: https://dotnetfiddle.net/JIVCVB
What is going wrong
additionalFields refers to the JArray of JObjects containing name and value JProperties. You are looping through this JArray to try to find the first JObject having a name property with a certain value, and when you find it you attempt to replace the JObject with a whole new JObject. You successfully remove the old JObject from the JArray, but when you are doing AddAfterSelf to insert the new JObject, you are referencing additionalFields (plural) not additionalField (singular). Recall that additionalFields is the JArray. So you are saying that you want to add the new JObject after the array. The array's parent is a JProperty called additionalFields. A JProperty can only have one value, so AddAfterSelf fails with the error you see.
How to fix your code
I think what you intended to do was additionalField.AddAfterSelf(updatedToken). However, this, too, will fail, for a different reason: you already removed the additionalField from the JArray at that point, so it no longer has a parent context. You would need to AddAfterSelf before you remove the item you are trying to insert after. If you fix that, you still have another problem: your loop doesn't break out after you've done the replacement, so then you will get an error about modifying the collection while looping over it.
Here is the relevant section of code with the corrections:
if (string.Equals(keyToken.First.ToString(), "NAME1", StringComparison.CurrentCultureIgnoreCase))
{
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalField.AddAfterSelf(updatedToken);
//remove the current token
additionalField.Remove();
// we found what we were looking for so no need to continue looping
break;
}
Fiddle: https://dotnetfiddle.net/KcFsZc
A simpler approach
You seem to be jumping through a lot of hoops to accomplish this task. Instead of looping, you can use FirstOrDefault to find the object you are looking for in the array. Once you've found it, you don't need to replace the whole object; you can just update the property values directly.
Here's how:
var rootObject = JToken.Parse(json);
// Get a reference to the array of objects as before
var additionalFields = rootObject["additionalFields"];
// Find the object we need to change in the array
var additionalField = additionalFields.FirstOrDefault(f =>
string.Equals((string)f["name"], "NAME1", StringComparison.CurrentCultureIgnoreCase);
// if the object is found, update its properties
if (additionalField != null)
{
additionalField["name"] = "newname";
additionalField["value"] = "newvalue";
}
Working demo: https://dotnetfiddle.net/ZAKRmi

How to deserialize encapsulated table of objects in .NET Core from JSON file?

I want to retrieve a collection of football leagues from an external api. The response from the server comes as shown below:
{
"api": {
"results": 1496,
"leagues": [
{
"league_id": 1,
.....
The returned object constists of an "api" field which hold "results" and "leagues". I would like deserialize the code and map it to League class objects in my code.
var jsonString = await ExecuteUrlAsync(filePath, url);
var results = JsonConvert.DeserializeObject<IEnumerable<LeagueEntity>>(jsonString);
jsonString is correct, but when the program hits second line I get an exception:
Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.IEnumerable".
I need to get to the "leagues" field in JSON file, and ignore the rest of the response. How to achieve that?
Assuming your LeagueEntity corresponds to the api.leagues[*] objects, you can use JsonConvert.DeserializeAnonymousType() to pick out and deserialize the interesting portions of the JSON:
var leagues = JsonConvert.DeserializeAnonymousType(jsonString,
new { api = new { leagues = default(List<LeagueEntity>) } })
?.api?.leagues;
This avoids the need to create an explicit data model for the api.leagues container objects. It should also be more efficient than pre-loading into a JToken hierarchy, then as a second step selecting and deserializing the api.leagues array.
Demo fiddle here.
(Alternatively, you could auto-generate a complete data model for the entire JSON using one of the answers from How to auto-generate a C# class file from a JSON string.)

Replace Json properties with NewtonSoft

No idea where to begin with this, so I don't have any sample code.
I need to change the name of a property in a json document.
var json = (#"{""id"":""12"",
""title"":""My Title"",
""Chunks"":[
{
""id"":""137"",
""title"":""Title"",
""description"":""null"",
""selections"":[
{
""id"":""169"",
""title"":""Choice"",
""sort_order"":""null"",
""questions"":[
]
}
]
}
]
}
}
}");
I need to change the "id" that's got the value of 12 to "document_id" and leave the other ids alone. Are there any C# libraries like NewtonSoft that allow you to change the property rather than the property value. Seems like a common scenario but I haven't seen anything close to what I'm trying to do. I suppose I could convert the json to a string and do a replace, but that doesn't seem very elegant.
An approach using Newtonsoft.Json.Linq.JObject would look something like:
var obj = JObject.Parse(json);
obj["document_id"] = obj["id"]; // create new property called "document_id"
obj.Remove("id"); // remove the "id" property
Console.WriteLine(obj);
Also note that your JSON is not valid. It has two extra } at the end.
Assuming you would want to replace all the keys when there could be more than one node with key as "id" and value "12", you could use Linq to identify Tokens with Key "Id" and Value "12" and then use Add/Remove methods for creating a new node with different name.
For example,
JToken node = JToken.Parse(json);
var jObjectsWithTitle = node
.SelectTokens("$..*")
.OfType<JObject>()
.Where(x => x.Property("id") != null && Convert.ToInt32(x.Property("id").Value) == 12);
foreach(var item in jObjectsWithTitle)
{
item.TryGetValue("id",out var currentValue);
item.Add("document_id",currentValue);
item.Remove("id");
}

Get the path of a key from nested JSON using Json.Net

I have a big nested JSON. I don't know the structure of the JSON.
I just have a set of keys which are present in the JSON but I don't know where exactly in the JSON.
How do I find out the path of a key from an unknown JSON structure assuming the key exists somewhere in it?
If your JSON structure is unknown, you can parse it into a JToken like this:
JToken token = JToken.Parse(json);
From there, you can use either SelectToken() or SelectTokens() with a recursive descent JsonPath expression to find the property (or properties) matching a key:
JToken match = token.SelectToken("$.." + keyToFind);
Once you have the matching token, you can get the path to it using its Path property:
string path = match?.Path;
Here is a working demo which assumes you have multiple keys to find and each key can appear multiple times in the JSON: https://dotnetfiddle.net/9Em9Iq
For an unknown structure you can iterate over the objects :
var reader = new JsonTextReader(new StringReader(jsonText))
while (reader.Read())
{
// Do a condition on the variables reader.TokenType, reader.ValueType, reader.Value
}
This method will log all paths in your top level json that have a key equal to "key"
var keys = jobject.Properties().Where(p => p.Name == key).ToList();
keys.ForEach(i => Console.WriteLine(i.Path));
This will NOT work in a recursive way but it is easy from this to do a recursive search from there
you can use
JObject o = JObject.Parse(<yourjson>);
dynamic obj = o.SelectTokens("$..Product");

Converting a Javascript JSON.stringfy string to an object using c# JSON.NET

I am developing a windows 8 app, and i have some javascript that stores a serialized object into roaming settings, i.e:
var object = [{"id":1}, {"id":2}]
roamingSettings.values["example"] = JSON.stringify(object);
I also i have a c# part to the application (for running a background task), that needs to read that JSON, and turn it into an object so i can iterate over it. And this is where i am having some issues, i am using JSON.NET to do the work, but every thing i turn turns up with an error:
// this looks like "[{\"id\":1},{\"id\":2}]"
string exampleJSON = roaming.Values["example"].ToString();
// dont know if this is correct:
List<string> example = JsonConvert.DeserializeObject<List<string>>(exampleJSON );
That give an error of:
Error reading string. Unexpected token: StartObject. Path '[0]', line 1, position 2.
So i am at a loss of what to do, i have been working on it for last few hours, and i am quite unfamiliar with c#, so resorting to the help of stackoverflow ;D
Thanks in advance for any help :)
Andy
Json.Net has a nice method DeserializeAnonymousType. No need to declare a temporary class.
string json = "[{\"id\":1},{\"id\":2}]";
var anonymous = new []{new{id=0}};
anonymous = JsonConvert.DeserializeAnonymousType(json,anonymous);
foreach (var item in anonymous)
{
Console.WriteLine(item.id);
}
You can even use the dynamic keyword
dynamic dynObj = JsonConvert.DeserializeObject(json);
foreach (var item in dynObj)
{
Console.WriteLine(item.id);
}
You are trying to parse your JSON array into a List of strings, which doesn't work. The JSON object you provide is actually a list of objects containing an integer property called 'id'.
Perhaps try creating a class (say, MyClass) with just that property, and deserialize into List.
Your json containts a collection of objects with an id property, something like this:
class IdObject {
public int id { get; set; }
}
You could then do:
JsonConvert.DeserializeObject<List<IdObject>>(exampleJSON);
Because the IdObject class has a property id to match your json serialized value, it will be mapped back.

Categories

Resources