0

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

  • jq '{actions[2].failCount}'
  • jq '{actions[2] failCount}'
  • jq '{actions .[2].failCount}'
  • etc.

    to get { "failCount" : "1" } ?

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}
Wimateeka
  • 2,474
  • 2
  • 16
  • 32

3 Answers3

3

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})
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • This is essentially the closest thing I am looking for. I didn't want to have to specify the keys all over again, like this: `{result, "failCount": .actions[2].failCount, "skipCount": .actions[2].skipCount, "totalCount": .actions[2].totalCount }`. I wanted to just pull from the api itself so I wouldn't have to make key changes if the api changes for some reason. Thanks. – Wimateeka Jun 20 '17 at 17:33
0

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"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
0

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.

peak
  • 105,803
  • 17
  • 152
  • 177