Get absolute path of JSON object - c#

I have a JSON object:
{
""settings"": {
""general"": {
""database"": { ""type"": ""PostgreSql"" }
}
}
}
The absolute path of my JSON object would look like this: settings/general/database/type
I tried to get all the keys with the first solution of this question:
IList<string> keys = parent.Properties().Select(p => p.Name).ToList();
This didn't work for me. The keys list only contained the first key settings and hasn't got the other ones.
There is a path property which shows the path of the node you are in but it doesn't show the complete path of the JSON object.
How can I get the absolute path like in my example?

In your question you are asking how to get the path to "my JSON object", but your sample JSON actually contains four objects, nested. (Each object begins with { and ends with } in the JSON.) So the path will be different depending on which JSON object you are referring to. It looks like you currently have a reference to the outermost object which you are querying to get the names of its properties. But this won't give you the descendant properties, as you've seen. What you need is to get the Path from the innermost property.
So I think we can boil your question down to this:
Given some JSON, how do I get the full path(s) to the deepest value(s) within the hierarchy (i.e. the leaf nodes)?
You can do that with this LINQ-to-JSON query:
var obj = JObject.Parse(json);
var paths = obj.DescendantsAndSelf()
.OfType<JProperty>()
.Where(jp => jp.Value is JValue)
.Select(jp => jp.Path)
.ToList();
If you only want the first one, then replace .ToList() with .FirstOrDefault().
Note that the path(s) will be returned with dots as delimiters. If you would prefer slashes, then add .Replace('.', '/') to jp.Path within the Select() method call.
Working demo here: https://dotnetfiddle.net/lFXtEE

Your parent object only has one key and that is "settings". Its value is a json object. That object has only one key and that is "general". Its value is a json object. That object has only one key and that is "database". Its value is a json object. You are using nested objects so you have to be specific about 'wanting all the keys' of which object.

The SO answer you referenced is not working because it is only giving you all the keys in your root object as a list of strings.
What you want is a recursive way to get all the "keys" (JProperty.Name).
If you have your JProperty with Name = "type", let us call it JProperty typeProp;. Then typeProp.Parent will get you the JContainer containing ""type"" : ""PostgreSql"" and typeProp.Parent.Parent will get you a JProperty with Name = "database".
So something like this might help (beware, untested):
JToken current = typeProp;
string path = "";
while (current != null)
{
path = current.Name + "/" + path;
if(current.Parent != null) current = current.Parent.Parent;
}
This will leave you with an extra slash on the end like this:
settings/general/database/type/
which you can remove with:
char[] charsToTrim = {'/'};
path.trimEnd(charsToTrim)

Related

How to get a nested object from an json parent array in c#

In a C# Controller.cs how do I get a "child" or "nested" object from a "parent" object. I am currently able to retrieve the values for objects that are NOT nested, but I also need to be able to grab the values to certain child objects as well.
Please see my currently working code below that actually retrieves the data when the object is NOT nested. I am able to get the values invoiceNumber, email, and paymentMethod .
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
var obj = JObject.Parse(result);
string invoiceNumber = (string)obj["invoiceNumber"];
string email = (string)obj["email"];
string paymentMethod = (string)obj["paymentMethod"];
}
Now when I attempt to get the data for objects that are nested, I just get retuned NULL. These values are NOT actually null as when I am debugging in visual studio JSON Visualizer, they have values but I just do not know how to write the syntax I need in order to grab these that are "child" or "nested" objects. So as you can see in the code below I tried to call this value for amountSaved in every way that I could think of, but it just returns NULL each time.
string rate = (string)obj["discounts.[0].amountSaved"];
string rate2 = (string)obj["discounts.amountSaved"];
string rate3 = (string)obj["discounts amountSaved"];
string rate4 = (string)obj["discounts:amountSaved"];
string rate5 = (string)obj["{discounts}:amountSaved"];
string rate6 = (string)obj["{discounts}.amountSaved"];
string rate7 = (string)obj["discounts.{amountSaved}"];
string rate8 = (string)obj["{discounts.amountSaved}"];
I figured the solution might be something like I call the parent object first which in this case is called discounts and then after a period or colon or space(. :) or something, I enter the name of the child object which in this case is amountSaved. That did not work.
Please see my screenshot below. This is when I debugging in visual studio and use the JSON Visualizer. You can see what I mean by discounts being a parent object with child values such as amountSaved. You can also see that it has a value of 0.75. You can can also see what I mean by the other values that are not nested which I am able to grab like shippingMethod.
How can I accomplish this?
Thank you.
when you use the square brakets you should not put . between them. And you can omit square brakets and use dot syntax only if you have deserialized object, not just parsed it
string rate = (string)obj["discounts"][0]["amountSaved"];

Get JProperty from Json Path in Newtonsoft.JSON

I have a JSON path string. Let's say for example:
Property1.Item2 and source JSON of
{
Property1 {
Item1: "Value123",
Item2: "Value111"
}
}
and fetch like so
var property = loadedJson.SelectToken(jsonPath);
// property in this case would be a JValue whose value is "Value111"
I want to select the JProperty pointed to by the path. It currently grabs only the value (JValue). This seems like it would be straight forward, but I cannot find a solution.
After selecting the value token you can use Parent to navigate to the property:
var property = (JProperty)loadedJson.SelectToken(jsonPath)?.Parent;
Fiddle: https://dotnetfiddle.net/lmbEGb

c#: read value from json using JToken

I have a json and I want to get a value from a complex object. Here is my json:
{
"a1" : {
"a2" : {
"a3" : "desired_value"
}
}
}
And I want to obtain the value "desired value". I've tried to obtain it with this code:
var token = JToken.Parse(json);
string value = token.Value<string>("a1.a2.a3");
But the value string is null. What should I put in the path so I can read this only once, I mean without getting a token and then iterate trough it's childern and so?
You can use SelectToken in order to select the property using its path and then extract the value from that:
string value = token.SelectToken("a1.a2.a3").Value<string>();
SelectToken will return null if the path isn't found, so you might want to guard against that.
JToken.Value expects a key to be provided, whereas SelectToken supports both keys and paths (a key is a simple path). You are providing a path where a key is expected, which results in a value not being found.
To demonstrate the difference, you could also retrieve the value of a3 like this:
token.SelectToken("a1.a2").Value<string>("a3");
I wouldn't recommend doing it this way, but it does show how paths involve traversal but keys are simple indexers.

Json.NET Convert model to array and return to JObject

I am having trouble finding the JObject nested keys in order to convert the key values to an array. The code below seems to only get the parent keys instead of the entire object.
What I want: Convert any ValuesToList items in the JObject to JArray if they are not already an array irregardless of how nested the keys are. NOTE: Solution should work with entirely different Json models.
Purpose: Sometimes the Json returns as a model instead of an array which causes deserializing to a model to throw exceptions.
result = {{ "errors": null, "content": { "officeId": 1, "daysClosed": [] } }}
// values in ValuesToList are "content" and "daysClosed"
var result = JObject.Parse(_responseString);
foreach (string item in ValuesToList.ToArray())
{
// Check to see if toList value is contained in the JObject.
if (result[item] != null)
{
// Check to see if the value is an array. If not, will convert to array/list Json.
if (!(result[item] is JArray))
result[item] = new JArray(result[item]);
}
}
The problem is that I am unable to find daysClosed in the JObject because it is a child. Thus, when item = "daysClosed" the result[item] will return null.
I was working on a way to use reflection to solve this problem but ran into issues where a new JObject would be appended to the current JObject and I am looking to replace the Keys values with an array if the keyvalue exists and is not already an array.
Since your keys can occur at any depth in the JObject, you'll need to use SelectTokens along with a JsonPath expression to find them. Something like this should work:
foreach (string item in ValuesToList.ToArray())
{
// find all occurrences of the item in the JObject at any depth
foreach (JToken match in result.SelectTokens("$.." + item))
{
// if the matching token is not an array, wrap it in an array
if (match.Type != JTokenType.Array)
{
JProperty parent = (JProperty)match.Parent;
parent.Value = new JArray(match);
}
}
}
Fiddle: https://dotnetfiddle.net/EtDqNs
why don't you try doing this:
var arrayProperties = result.DoSomething().OfType<JProperty>().Where(prop => prop.Value.Type == JArray);

JSON.net parent key

I'm having a hard time getting the parent key/property/attribute of my JSON objects using JSON.net. That is, I want the outermost property name, as a string, without knowing beforehand/visually what it is. I'm currently iterating over a set of KeyValuePair items and attempting to, for each of those, log out the parent from something that looks like
{"parentKey":
{
"Name": "Name",
"Id": "123",
"Other": null,
"nestedArr":
[
"idx0",
"idx1",
"idx2"
]
}
}
I've tried both keyValue.Value.Ancestors() and keyValue.Value.Parent. With the former, I'm getting what looks to be the function definition... I'm actually not sure what it is: Newtonsoft.Json.Linq.JToken+<GetAncestors>d_ _ 42. Completely beffuddled by that, because based on the usage examples I've scrounged up here, I'm using it to standard.
With the latter, I log out the entire object, or else what appears to be the entire preceding KeyValuePair, rather than just the string "parentKey", which is what I want. The JSON.net docs aren't the best as far as explicit usage examples and what to expect (or maybe it's just that being new to C#, I can't make sense of them), but in any case, I'm kind of unclear on why this is happening and how to accomplish what I want. This is what I'm trying:
foreach (var keyValue in jObjList[0]) //jObjList is a List<JObject> defined above
{
Console.WriteLine(keyValue.Value.Ancestors());
Console.WriteLine(keyValue.Value.Parent);
if (keyValue.Value.GetType() == typeof(JObject))//same block goes for if it's typeof(JArray)
{
Console.WriteLine(keyValue.Key);
}
}
Edit: in the JSON given, and within the loop defined above, for example, in order to get my parent keys (that's just what I'm calling them), my code simply says, if (keyValue.Value.GetType() == typeof(JObject), write keyValue.Key to the console, and the same goes for if getType() is a JArray. In either case, keyValue.Key is a parent key, if that makes sense. What I mean to say by this is that it is a property that points to another Array or Object. My issue is that, as I'm doing this loop recursively, when I get down to a nested Array or Object, my code has no way of realizing that, although there is a new "parent key" currently, like with nestedArr, for example, the parent key of nestedArr is still "parentKey".
the code is abridged, but that's the idea.
All clarifications and corrections are welcome and appreciated. Thanks.
You are seeing Newtonsoft.Json.Linq.JToken+<GetAncestors>d_ _ 42 for Console.WriteLine(keyValue.Value.Ancestors()) because Ancestors is an IEnumerable<T> whose evaluation is lazy, rather than an explicit collection. What you are seeing is the ToString() output of the not-yet-evaluated enumerable.
If what you want to do is to climb up the parent list of a given JToken and find the lowest parent that has a "parentKey" property, then get the value of that parentKey, then this is how you would do it:
JToken token = keyValue.Value; // Here I'm declaring JToken explicitly for clarity. Normally I would use var token = ...
var parentKey = token.AncestorsAndSelf() // Climb up the json container parent/child hierachy
.Select(p => p.SelectToken("parentKey")) // Get the "parentKey" property in the current parent (if present)
.FirstOrDefault(k => k != null); // Return the first one found.
Console.WriteLine(parentKey);
Update
To get the name of the JSON property highest in the JSON container hierarchy, you would do:
var name = token.AncestorsAndSelf() // Walk up the list of ancestors
.OfType<JProperty>() // For each that is a property
.Select(p => p.Name) // Select the name
.LastOrDefault(); // And return the last (topmost).
Update 2
If you're looking for the first property name that appears in a JSON file, you can do the following, using JContainer.DescendantsAndSelf():
var json = #"[{""parentKey"":
{
""Name"": ""Name"",
""Id"": ""123"",
""Other"": null,
""nestedArr"":
[
""idx0"",
""idx1"",
""idx2""
]
}
}]";
var root = (JContainer)JToken.Parse(json);
var name = root.DescendantsAndSelf() // Loop through tokens in or under the root container, in document order.
.OfType<JProperty>() // For those which are properties
.Select(p => p.Name) // Select the name
.FirstOrDefault(); // And take the first.
Debug.WriteLine(name); // Prints "parentKey"
(JContainer represents a JSON node that can contain child nodes, such as an object or array.)

Categories