Wakan Tanka
Wakan Tanka

Reputation: 8042

get full path up to given element

I have exported bookmarks from Firefox which in my case is quiet large JSON. I'm interested in the object with guid _0no-4e9woLW in a JSON such as this one:

{
  "foo": {
    "bar": {
      "zoo": {
        "guid": "_0no-4e9woLW",
        "title": "frontend",
        "index": 3,
        "dateAdded": 1614198626454000,
        "lastModified": 1619505016472000,
        "id": 16518,
        "typeCode": 2,
        "type": "text/x-moz-place-container",
        "children": [
          {
            "guid": "2L7ZsYTM2RX0",
            "title": "css child override parent style",
            "index": 0,
            "dateAdded": 1614198648005000,
            "lastModified": 1614198648005000,
            "id": 16519,
            "typeCode": 1,
            "type": "text/x-moz-place",
            "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+override+parent+style"
          },
          {
            "guid": "x3JHTAezzyI8",
            "title": "css child parent inheritance",
            "index": 1,
            "dateAdded": 1614198648096000,
            "lastModified": 1614198648096000,
            "id": 16520,
            "typeCode": 1,
            "type": "text/x-moz-place",
            "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+parent+inheritance"
          }
        ]
      }
    }
  }
}

Is it possible to get somehow full path up to "guid": "_0no-4e9woLW"? guid keys are uniq in JSON. My goal is to type something like jq '["full path I've somehow previously obtained"]["children"].title'

And I will obtain:

css child override parent style
css child parent inheritance

Of course I could do this manually but I'm too lazy.

Upvotes: 0

Views: 456

Answers (4)

tomc
tomc

Reputation: 1207

json2jqpath.jq file.json 
.
.foo
.foo|.bar
.foo|.bar|.zoo
.foo|.bar|.zoo|.children
.foo|.bar|.zoo|.children|.[]
.foo|.bar|.zoo|.children|.[]|.dateAdded
.foo|.bar|.zoo|.children|.[]|.guid
.foo|.bar|.zoo|.children|.[]|.id
.foo|.bar|.zoo|.children|.[]|.index
.foo|.bar|.zoo|.children|.[]|.lastModified
.foo|.bar|.zoo|.children|.[]|.title
.foo|.bar|.zoo|.children|.[]|.type
.foo|.bar|.zoo|.children|.[]|.typeCode
.foo|.bar|.zoo|.children|.[]|.uri
.foo|.bar|.zoo|.dateAdded
.foo|.bar|.zoo|.guid
.foo|.bar|.zoo|.id
.foo|.bar|.zoo|.index
.foo|.bar|.zoo|.lastModified
.foo|.bar|.zoo|.title
.foo|.bar|.zoo|.type
.foo|.bar|.zoo|.typeCode

Shows guid appears as a leaf in more than one unique path. If there were only one you would be done (assuming you knew the value existed) with multiple paths you need to check that the value exists and it is to your object of interest.

json2jqpath.jq file.json | grep guid
.foo|.bar|.zoo|.children|.[]|.guid
.foo|.bar|.zoo|.guid

Appending your given value to the paths returns objects containg them

jq '.foo|.bar|.zoo|.children|.[]|.guid="_0no-4e9woLW"' file.json 
{
  "guid": "_0no-4e9woLW",
  "title": "css child override parent style",
  "index": 0,
  "dateAdded": 1614198648005000,
  "lastModified": 1614198648005000,
  "id": 16519,
  "typeCode": 1,
  "type": "text/x-moz-place",
  "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+override+parent+style"
}
{
  "guid": "_0no-4e9woLW",
  "title": "css child parent inheritance",
  "index": 1,
  "dateAdded": 1614198648096000,
  "lastModified": 1614198648096000,
  "id": 16520,
  "typeCode": 1,
  "type": "text/x-moz-place",
  "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+parent+inheritance"
}


jq '.foo|.bar|.zoo|.guid="_0no-4e9woLW"' file.json 
{
  "guid": "_0no-4e9woLW",
  "title": "frontend",
  "index": 3,
  "dateAdded": 1614198626454000,
  "lastModified": 1619505016472000,
  "id": 16518,
  "typeCode": 2,
  "type": "text/x-moz-place-container",
  "children": [
    {
      "guid": "2L7ZsYTM2RX0",
      "title": "css child override parent style",
      "index": 0,
      "dateAdded": 1614198648005000,
      "lastModified": 1614198648005000,
      "id": 16519,
      "typeCode": 1,
      "type": "text/x-moz-place",
      "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+override+parent+style"
    },
    {
      "guid": "x3JHTAezzyI8",
      "title": "css child parent inheritance",
      "index": 1,
      "dateAdded": 1614198648096000,
      "lastModified": 1614198648096000,
      "id": 16520,
      "typeCode": 1,
      "type": "text/x-moz-place",
      "uri": "https://www.google.com/search?client=firefox-b-d&q=css+child+parent+inheritance"
    }
  ]
}

json2jqpath.jq

Upvotes: 0

ikegami
ikegami

Reputation: 385506

  • To access a node by guid:

    .. | objects | select( .guid == … )
    

    So you'd use the following to get the title of _0no-4e9woLW's children:

    .. | objects | select( .guid == "_0no-4e9woLW" ) | .children[].title
    

    Demo on jqplay

  • To save a path:

    path( … )               # As JSON
    
    path( … ) | join(".")   # As dotted path
    

    Replace with an expression that returns the node(s) for which you want the path.

    For example,

    path( .. | objects | select( .guid == "_0no-4e9woLW" ) )   # [ "foo", "bar", "zoo" ]
    

    Demo on jqplay

    path( .. | objects | select( .guid == "_0no-4e9woLW" ) ) | join(".")   # "foo.bar.zoo"
    

    Demo on jqplay

  • To grab an object using its path:

    getpath( … )                                      # From JSON
    
    getpath( … | split(".") | map(tonumber? // .) )   # From dotted path
    

    Replace with an expression that returns the previously saved path.

    For example,

    getpath( [ "foo", "bar", "zoo" ] ).children[].title
    

    Demo on jqplay

    getpath( "foo.bar.zoo" | split(".") | map(tonumber? // .) ).children[].title
    

    Demo on jqplay

Upvotes: 1

Philippe
Philippe

Reputation: 26397

Using recurse allows to get to the object easily :

jq -r 'recurse |
       select(type=="object" and .guid == "_0no-4e9woLW")
       .children[].title' firefox.json

Upvotes: 1

Inian
Inian

Reputation: 85530

Without the whole knowledge of the JSON, something like this should work when using path family of functions i.e. getpath/1

( paths | select(.[-1] == "guid") ) as $p | 
  if getpath($p)  == "_0no-4e9woLW" then getpath($p[:-1] + ["children"])[].title else empty end

The construct above means, select all those paths containing the leaf key named guid and store them in a variable and filter only the path whose value turns out to be "_0no-4e9woLW". On the selected path, trim the child node($p[:-1] removes "guid") and add the node "children" and get the value at the new path constructed (using getpath/1). Extract the .title field from the JSON value

jqplay demo

Upvotes: 2

Related Questions