Home > Back-end >  Access to first key of a nested json using jq and obtain its value
Access to first key of a nested json using jq and obtain its value

Time:01-20

I have the following JSON:

{
    "query": "rest ec",
    "elected_facts_mapping": {
        "AWS": {
            "ECS": {
                "attachments": [
                    "restart_ecs"
                ],
                "text": [
                    "Great!"
                ]
            }
        }
    },
    "top_facts_mapping": {
        "AWS": {
            "ECS": {
                "attachments": [
                    "restart_ecs"
                ],
                "text": [
                    "Great!"
                ]
            },
            "EC2": {
                "attachments": [
                    "create_ec2"
                ],
                "text": [
                    "Awesome"
                ]
            }
        },
        "GitHub": {
            "Pull": {
                "attachments": [
                    "pull_req"
                ],
                "text": [
                    "Be right on it"
                ]
            }
        },
        "testtttt": {
            "test": {
                "attachments": [
                    "hello_world"
                ],
                "text": [
                    "Be right on it"
                ]
            }
        },
        "fgjgh": {
            "fnfgj": {
                "attachments": [
                    "hello_world"
                ],
                "text": [
                    "Be right on it"
                ]
            }
        },
        "tessttertre": {
            "gfdgfdgfd": {
                "attachments": [
                    "hello_world"
                ],
                "text": [
                    "Great!"
                ]
            }
        }
    },
    "elected_facts_with_prefix_text": null
}

And I want to access to top_facts_mapping's first key AWS and it's first key ECS

I am trying to do this (in my DSL):

'.span | fromjson'
'.span_data.top_facts_mapping | keys[0]'
'.span_data.top_facts_mapping[${top_facts_prepare_top_fact_topic}] | keys[0]'
'.top_facts_prepare_top_fact_topic_subtopic[${top_facts_prepare_top_fact_topic}][${top_facts_prepare_top_fact_topic_subtopic}]'

CodePudding user response:

You could use the keys_unsorted builtin, since the underlying object is a dictionary and not a list

.top_facts_mapping | keys_unsorted[0] as $k | .[$k] | .[keys_unsorted[0]]

The above filter could be re-written with a simple function

def get_firstkey_val: keys_unsorted[0] as $k | .[$k];
.top_facts_mapping | get_firstkey_val | get_firstkey_val

Or with some jq trick-play, assumes the path provided top_facts_mapping is guaranteed to exist

getpath([ paths | select(.[-3] == "top_facts_mapping" ) ] | first)

Since the paths built-in constructs the root to leaf paths as arrays, we all paths containing the second to last field (denoted by .[-3]) as "top_facts_mapping" which returns paths inside it

From which first selects the first entity in the list i.e. below list

[
  "top_facts_mapping",
  "AWS",
  "ECS"
]

Use getpath/1 to obtain the JSON value at the obtained path.

If there is a risk of the key top_facts_mapping not being present in the JSON, getpath/1 could return an error as written above. Fix it by adding a proper check

([ paths | select(.[-3] == "top_facts_mapping" ) ] | first) as $p | 
if $p | length > 0 then getpath($p) else empty end

CodePudding user response:

You could use to_entries to turn the object into an array of key-value pairs, then select the first value using [0].value

.top_facts_mapping | to_entries[0].value | to_entries[0].value
{
  "attachments": [
    "restart_ecs"
  ],
  "text": [
    "Great!"
  ]
}

Demo

If at one level the object may be empty, you can prepend each to_entries with try (optionally followed by a catch clause)

CodePudding user response:

Here's a stream-based approach which disassembles the input using the --stream option, filters for the "top_facts_mapping" key on top level .[0][0], truncates the stream to descend 3 levels, re-assembles the stream using fromstream, and outputs the first match:

jq --stream -n 'first(fromstream(3| truncate_stream(inputs | select(.[0][0] == "top_facts_mapping"))))'
{
  "attachments": [
    "restart_ecs"
  ],
  "text": [
    "Great!"
  ]
}
  • Related