0

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.

enter image description here

How can I get the absolute path like in my example?

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
Nightscape
  • 464
  • 6
  • 19
  • 1
    The absolute path to _what_? Where do you define you want the value of "type"? Anyway see https://www.newtonsoft.com/json/help/html/QueryJsonSelectTokenJsonPath.htm, https://stackoverflow.com/tags/jsonpath – CodeCaster Oct 23 '19 at 12:30
  • i don't want the value of "type". I want all the keys of my json object and then connect them to a path. – Nightscape Oct 23 '19 at 12:32

3 Answers3

5

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

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
1

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.

TheMikeInNYC
  • 423
  • 3
  • 4
  • I didn't notice this. To be exact i am trying to get every single key of the nested object. Like you said I want the key "settings" ob the parent object. It's value is a json object. That object has only one key and that is "general". I also want this. At the end i would have the keys in this order: settings, general, database, type – Nightscape Oct 23 '19 at 13:49
1

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)
Daniklad
  • 945
  • 8
  • 10