Wimateeka
Wimateeka

Reputation: 2696

Use jq to grab specific key:value pair from nth object in a JSON array

Using this JSON taken from a Jenkins build api call via curl

{
   "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",
   "actions" : [
       {
           "_class" : "hudson.model.CauseAction",
           "causes" : [
                {
                    "_class" : "jenkins.branch.BranchIndexingCause",
                    "shortDescription" : "Branch indexing"
                }
            ]
        },
        {
            "_class" : "hudson.model.ParametersAction",
            "parameters" : [ "..." ]
        },
        {
            "_class" : "hudson.tasks.junit.TestResultAction",
            "failCount" : 1,
            "skipCount" : 14,
            "totalCount" : 222,
            "urlName" : "testReport"
        }
    ],
    "artifacts" : [ "..."  ],
    "result" : "UNSTABLE",
    "previousBuild" : {
        "number" : 98,
        "url" : "<some Url>"
     }
}

Why can I do jq '{result}' <fileNameWithJSON> and get

{ "result" : "UNSTABLE" }

But I cannot do jq '{.actions[2] failCount}' <fileNameWithJSON> or other variations such as

I want to grab the result, as well as actions[2] failCount, actions[2] skipCount and actions[2] totalCount to create a new JSON like this:

{ "result" : "UNSTABLE","failCount" : 1, "skipCount" : 14,"totalCount" : 222}

EDIT:

My goal was to not have to re-specify the keys in case they changed in the api. I essentially didn't want this:

{result, "failCount":.actions[2].failCount, "skipCount":.actions[2].skipCount, "totalCount": .actions[2].totalCount}

Upvotes: 0

Views: 741

Answers (3)

peak
peak

Reputation: 116880

My goal was to not have to re-specify the keys in case they changed in the api.

If that is one of the main goals, you might want to consider an approach exemplified by the following, which also makes no assumption about which element of .actions contains the information of interest:

{ result } + (.actions[] | select(has("failCount")))

With your example data, this would produce:

{
  "result": "UNSTABLE",
  "_class": "hudson.tasks.junit.TestResultAction",
  "failCount": 1,
  "skipCount": 14,
  "totalCount": 222,
  "urlName": "testReport"
}

If you don't want some of the extra fields, you can delete them, e.g. if you definitely do not want "_class", you can add del(._class) to the pipeline.

Upvotes: 0

Charles Duffy
Charles Duffy

Reputation: 295679

The {} syntax is sugar. It's intended to be used as a shortcut when you need a simple expression, but there's no reason to use that same shortened syntax when what you actually want is more interesting.

jq '
  .actions[2] as $a2 |              # assign second action to a variable
  { "result": .result,              # refer direct to original input when appropriate...
    "skipCount": $a2.skipCount,     # ...or to that variable otherwise.
    "failCount": $a2.failCount,
    "totalCount": $a2.totalCount}
' <<<"$json"

Upvotes: 0

Jeff Mercado
Jeff Mercado

Reputation: 134521

jq can only copy direct fields from one object to another in object literals. It wasn't programmed to go any deeper than that though it is most certainly possible in other languages that support this kind of feature.

If your goal is to minimize the repetition of the property names, you will just have to rewrite the filter a bit.

{result} + (.actions[2] | {failCount,skipCount,totalCount})

Upvotes: 3

Related Questions