Search code examples
arraysjsonexport-to-csvibm-mqjq

Using jq to produce one line of output per array element in a json object that also includes other fields from the json object


I'm trying to format some data that consists of json objects that includes some identifying information along with an array of one or more json objects, I would like the result to be one line of data per array element where each line should include some fields from the array element and some fields from the identifying information.

My sample data is below:

{
  "eventCreation": {
    "timeStamp": "2020-06-06T15:07:20Z",
    "epoch": 1591456040
  },
  "eventData": {
    "applName": "SampleApp",
    "channelName": "SYSTEM.DEF.SVRCONN",
    "connectionName": "127.0.0.1",
    "channelType": "Svrconn",
    "remoteProduct": "MQJM",
    "remoteVersion": "09010005",
    "activityTrace": [
      {
        "operationId": "Get",
        "operationTime": "11:07:18",
        "qmgrOpDuration": 102,
        "reasonCode": {
          "name": "No Msg Available",
          "value": 2033
        },
        "objectName": "SYSTEM.DEFAULT.LOCAL.QUEUE"
      },
      {
        "operationId": "Cb",
        "operationTime": "11:07:18",
        "qmgrOpDuration": 10,
        "reasonCode": {
          "name": "None",
          "value": 0
        },
        "objectName": "SYSTEM.DEFAULT.LOCAL.QUEUE"
      },
      {
        "operationId": "Cb",
        "operationTime": "11:07:18",
        "qmgrOpDuration": 12,
        "reasonCode": {
          "name": "None",
          "value": 0
        },
        "objectName": "SYSTEM.DEFAULT.LOCAL.QUEUE"
      }
    ]
  }
}

I would like to get an output like this:

"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","11:07:18","Get",102,"SYSTEM.DEFAULT.LOCAL.QUEUE",2033
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","11:07:18","Cb",10,"SYSTEM.DEFAULT.LOCAL.QUEUE",0
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","11:07:18","Cb",12,"SYSTEM.DEFAULT.LOCAL.QUEUE",0

I can pick any one element of the array and get it to print 3 lines, but if I add a 2nd element it will print 9 lines, 3rd element prints 27, etc.

For example:

jq -r '{channelName: .eventData.channelName, channelType: .eventData.channelType, connectionName: .eventData.connectionName, applName: .eventData.applName, remoteProduct: .eventData.remoteProduct, remoteVersion: .eventData.remoteVersion, operationId: .eventData.activityTrace[].operationId}|[.[]]|@csv' TEST.json

Will produce this:

"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Get"
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb"
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb"

If I add a second like this:

jq -r '{channelName: .eventData.channelName, channelType: .eventData.channelType, connectionName: .eventData.connectionName, applName: .eventData.applName, remoteProduct: .eventData.remoteProduct, remoteVersion: .eventData.remoteVersion, operationId: .eventData.activityTrace[].operationId, qmgrOpDuration: .eventData.activityTrace[].qmgrOpDuration}|[.[]]|@csv' TEST.json

Will produce this:

"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Get",102
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Get",10
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Get",12
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",102
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",10
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",12
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",102
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",10
"SYSTEM.DEF.SVRCONN","Svrconn","127.0.0.1","SampleApp","MQJM","09010005","Cb",12

Solution

  • Using your approach, the following is a solution:

    .eventData
    | ({channelName, channelType, connectionName, applName, remoteProduct, remoteVersion}
       + ( .activityTrace[]
           | { operationTime, operationId, qmgrOpDuration, objectName, v: .reasonCode.value}))
    | [.[]]
    | @csv
    

    The key is to iterate just once.

    Notice also that this solution achieves its brevity in part by using the fact that {foo: .foo} can be abbreviated to {foo}.

    A slightly more efficient approach

    .eventData
    | [.channelName, .channelType, .connectionName, .applName, .remoteProduct, .remoteVersion]
      + ( .activityTrace[]
          | [.operationTime, .operationId, .qmgrOpDuration, .objectName, .reasonCode.value] )
    | @csv
    

    .operationTime first

    .eventData
    | [.channelName, .channelType, .connectionName, .applName, .remoteProduct, .remoteVersion] as $x
    | ( .activityTrace[] 
        | [.operationTime]
          + $x
          + [.operationId, .qmgrOpDuration, .objectName, .reasonCode.value] )
    | @csv