Get the name of a JObject in Json.Net - c#

I have a JObject equal to:
"Info":
{
"View":"A",
"Product":"B",
"Offer":"Offer1",
"Demo":"body {background-color:red;} #box {border:dotted 50px red;}",
"Log":false
}
How can I return the name of the object, "Info"?
I am currently using the Path property like so:
jObject.Name = jObject.Path.Substring(jObject.Path.jObject('.') + 1);
Is there a better way to do this?

In JSON, objects themselves do not have names. An object is just a container for a collection of name-value pairs, beginning and ending with curly braces. So what you have above is a fragment of a larger body of JSON. There must be an outer object to contain it. That outer object has a property with a name of Info, and the value of that property is the object you are referring to.
{
"Info":
{
"View":"A",
"Product":"B",
"Offer":"Offer1",
"Demo":"body {background-color:red;} #box {border:dotted 50px red;}",
"Log":false
}
}
In Json.Net, a JObject models a JSON object, and a JProperty models a name-value pair contained within a JObject. Each JObject has a collection of JProperties which are its children, while each JProperty has a Name and a single child, its Value.
So, assuming you have a reference to the innermost JObject (containing the View, Product and Offer properties), you can get the name of its containing JProperty like this:
JProperty parentProp = (JProperty)jObject.Parent;
string name = parentProp.Name; // "Info"

Related

How to empty a JObject array in C#

I have the following json
{
"audit_date": "2020-05-13T11:27:10.3187798Z",
"client_uuid": "2fd77dd8-ed76-4bba-b0e1-5cda454c8d6e",
"audit_entry": {
"where_uri": "test.com/dataservice/apps/171f0841-825b-4964-8f8c-0869650f14a6",
"why_uri": "test.com/dataservice/reference/reasons_for_change/61acc173-7168-4ae5-9f04- afa228941f8b",
"who_uri": "test.com/securityservice/users/4977dae1-a307-425f-980c-53413fef1b0f",
"when_audited": "2018-11-13T20:20:39+00:00",
"what_uri": "test.com/dataservice/study_subjects/1bc67a71-8549-4ab8-9dd9-e44238198860",
"what_changed": [
{
"attribute_name": "birth_year",
"attribute_value": "1969",
"attribute_change": null
},
{
"attribute_name": "subject_reference",
"attribute_value": "TEST-WOO3444",
"attribute_change": null
}
]
}
}
But I want to empty the nest array "what_changed"
So I need the output to be
{
"audit_date": "2020-05-13T11:27:10.3187798Z",
"client_uuid": "2fd77dd8-ed76-4bba-b0e1-5cda454c8d6e",
"audit_entry": {
"where_uri": "test.com/dataservice/apps/171f0841-825b-4964-8f8c-0869650f14a6",
"why_uri": "test.com/dataservice/reference/reasons_for_change/61acc173-7168-4ae5-9f04-afa228941f8b",
"who_uri": "test.com/securityservice/users/4977dae1-a307-425f-980c-53413fef1b0f",
"when_audited": "2018-11-13T20:20:39+00:00",
"what_uri": "test.com/dataservice/study_subjects/1bc67a71-8549-4ab8-9dd9-e44238198860",
"what_changed": []
}
}
I have written the following code
JObject jObj = JObject.Parse(jsonText);
jObj["audit_entry"]["what_changed"] = null;
string json = jObj.ToString(Formatting.None);
but this makes the field null rather than empty array.
I have also tried
JObject jObj = JObject.Parse(jsonText);
jObj["audit_entry"]["what_changed"] = "";
string json = jObj.ToString(Formatting.None);
but that still doesn't give an empty array.
I also tried using the Array.Clear() method, but this is a JObject array rather than a normal array.
Arrays are represented by the JArray type, not JObject. Cast the value of "what_changed" to the proper type and use the methods you need. Eg:
JObject jObj = JObject.Parse(jsonText);
JArray changed=(JArray)(jObj["audit_entry"]["what_changed"]);
changed.Clear();
Working with JSON elements is rather unusual though. It's typically a lot easier to deserialize JSON strings into strongly typed objects, modify them as needed and then serialize them back to a string.
Generating the necessary DTOs can be done easily in Visual Studio by selecting Paste Special > Paste JSON as Classes from the Edit menu
I can see several possibilities...
1.- instead of clearing the array, create a new one, an empty one. This does not solve the problem but it is a work around.
2.- using newtonsoft (a nuget package that you can download), you may be able to find different utilities there.
2.1.- Instead of parsing with JObject, parse with JArray, and then use Clear:
https://www.newtonsoft.com/json/help/html/ParseJsonArray.htm
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JArray_Clear.htm.
2.2.- use Remove, for this you need the property name, so you need to iterate within the array (a foreach), getting the name of the property, and delete one by one.
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JObject_Remove.htm
You need to cast it to a JArray first, then you can use its Clear() method;
((JArray)(jObj["audit_entry"]["what_changed"])).Clear();
Alternatively, you could simply create a new JArray in place of the old one.
jObj["audit_entry"]["what_changed"] = new JArray();
Try this :
JObject jObj = JObject.Parse(jsonText);
JToken jToken = jObj["audit_entry"]["what_changed"];
jToken.Replace(JToken.Parse("[]"));
string json = jObj.ToString(Formatting.None);

Unable to find certain data(json objects) in json response with JProperty

I'm trying to debug my JProperty object but somehow I can not get all the data out of this object. When I debug te JProperty it says it has the data im trying to recieve but I'm only able to obtain a part of it, explanation:
So my response JSON looks like this:
{"url":"www.microsoft0nline.nl\/test.php","time":"2019-06-19 09:50:49","stringvalue":"otherValue1","intvalue":433,"array":["35","37","43"],"otherObject1":{"name":"object1","description":"A object type","size":120}}
I'm iterating through this with the following code:
foreach (JProperty property in jsonResponse.Properties())
{
}
So when I run my code in debug mode and i debug these properties I'm getting the following:
As you all can see the parent of property has the object "otherObject1" within.
Now I iterate through this property with the following code:
foreach (var itemsOfResponse in property)
{
var dataJson = itemsOfResponse;
System.Diagnostics.Debug.WriteLine(dataJson);
}
And the response in my debugger looks like this:
www.microsoft0nline.nl/test.php
2019-06-19 10:01:49
otherValue1
433
[
"35",
"37",
"43"
]
The response contains all the values of my json response exept the one of "otherObject1", how is this possible? And is there a way to obtain the key values(url, stringvalue, intvalue, etc) of these properties to?
Hope someone can help me out here!
JProperty is only a single name value token pair.
JObject will contain many JToken objects (including JProperty and JObject instances).
For what you are trying to do, you should probably use JToken as both JProperty and JObject are JToken derived classes.

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");

Changing Key Of An Property in Json

I'm trying to make a json editor(works with treeview) these days, i did changing value function, i can change some keys as well, but i cant set keys in objects.
I can set the value:
SetValue(ref JObject main,JToken token,JToken newValue) {
//2nd argument is obj.SelectToken(node.Path)
token.Replace(newValue);
}
I can also set some keys:
SetKey(ref JObject main,JToken token,string newKey) {
//2nd argument is obj.SelectToken(node.Path)
//However, if token is in object, it seys the key of object because parent is object
(token.Parent as JProperty).Replace(newKey);
}
But how can i set the keys?
Regards.
You don't need to pass the original root object by ref and you don't need the original root at all. All you care about is the JToken and its parent.
In this case, you want to think of "replacement" as:
Add the old value by new key
Remove the old key/value pair
public void SetKey(JObject parent, JToken token, string newKey)
{
var tokenProp = token as JProperty;
var oldKeyName = tokenProp.Name;
parent[newKey] = tokenProp.Value;
parent.Remove(oldKeyName);
}
We can assume that if you are replacing a key for a key value pair, that the object is a JProperty token. In addition, if we are replacing keys, it is also safe to assume the parent is a JObject. You can call it as such:
var json = "{ 'key1': 'val1' }";
JObject parsedObj = JsonConvert.DeserializeObject<JObject>(json);
SetKey(parsedObj, parsedObj.First, "key2");
I had a similar issue where I had to remove whitespaces from all properties within a JObject, but didn't want to use a helper function. With using System.Linq:
var descendants = jObject.Descendants().Where(attr => attr is JProperty && ((JProperty)attr).Name.Contains(" ")).ToList();
descendants.ForEach(attr => attr.Parent[((JProperty)attr).Name.Replace(" ", string.Empty)] = ((JProperty)attr).Value);
descendants.ForEach(attr => attr.Remove());

Getting the error "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty" in Json.net

So I'm trying to control deserialization by reading a json object as a JObject, deleting some fields, and then deserializing it again to my target object using Json.Net. The problem is, any time I try to delete a field, I get the error:
An unhandled exception of type 'Newtonsoft.Json.JsonException'
occurred in Newtonsoft.Json.dll
Additional information: Cannot add or remove items from
Newtonsoft.Json.Linq.JProperty.
Here's my (simplified, but still causing the error) code:
JToken token = (JToken)JsonConvert.DeserializeObject(File.ReadAllText(fileName));
foreach (JToken inner in token["docs"])
{
if (inner["_id"] != null)
inner["_id"].Remove();
MyObject read = new MyObject();
JsonConvert.PopulateObject(inner.ToString(), read);
Values.Add((MyObject)JsonConvert.DeserializeObject(inner.ToString(), typeof(MyObject)));
}
The json is a very large file where the docs array contains many elements as follows (again simplified for clarity):
{
"docs": [
{
"Time": "None",
"Level": 1,
"_id": "10208"
},
{
"Time": "None",
"Level": 1,
"_id": "10209"
}
]
}
Alternatively if there's a better way to deserialize JSON to a specific type, but still ignoring additional fields, that would be a fine alternative.
Assuming Values is a List<MyObject> and your MyObject class looks like this:
class MyObject
{
public string Time { get; set; }
public int Level { get; set; }
}
you can replace all that code with the following to get the result you want:
string json = File.ReadAllText(fileName);
Values = JToken.Parse(json)["docs"].ToObject<List<MyObject>>();
This works because Json.Net will ignore missing properties by default. Since the MyObject class does not contain an _id property to deserialize into, you don't need to jump through hoops trying to remove it from the JSON.
Explanation of why Remove() didn't work
JToken.Remove() removes a JToken from its parent. It is legal to remove a JProperty from its parent JObject, or to remove a child JToken from a JArray. However, you cannot remove the value from a JProperty. A JProperty must always have exactly one value.
When you ask for token["_id"] you get back the value of the JProperty called _id, not the JProperty itself. Therefore you will get an error if you try to call Remove() on that value. To make it work the way you are doing, you'd need to use Parent like this:
if (inner["_id"] != null)
inner["_id"].Parent.Remove();
This says "Find the property whose name is _id and give me the value. If it exists, get that value's parent (the property), and remove it from its parent (the containing JObject)."
A more straightforward way to do it is to use the Property() method to access the property directly. However, this method is only available on JObject, not JToken, so you would either need to change the declaration of inner to a JObject or cast it:
foreach (JObject inner in token["docs"].Children<JObject>())
{
JProperty idProp = inner.Property("_id");
if (idProp != null)
idProp.Remove();
...
}
Lastly, as mentioned in the comments, if you're using C# 6 or later you can shorten the code a bit using the null-conditional operator:
inner.Property("_id")?.Remove();
Based on brillian answer from Brian, you can do simply this in your case:
var inner_id = inner["_id"] as JProperty;
if (inner_id != null)
inner_id.Remove();

Categories

Resources