8

I have a file that looks like this:

[
  {
    "billingAccountNumber": "x",
    "paymentResponseObject": {
      "uiErrorDipslayMessage": "",
      "transactionStatus": "S",
      "transactionDescription": "",
      "transactionCode": "",
      "confirmationNumber": "1"
    }
  },
  {
    "billingAccountNumber": "y",
    "paymentResponseObject": {
      "uiErrorDipslayMessage": "",
      "transactionStatus": "S",
      "transactionDescription": "",
      "transactionCode": "",
      "confirmationNumber": "2"
    }
  },
  {
    "billingAccountNumber": "z",
    "paymentResponseObject": {
      "uiErrorDipslayMessage": "",
      "transactionStatus": "S",
      "transactionDescription": "",
      "transactionCode": "",
      "confirmationNumber": "3"
    }
  }
]

The data doesn't look exactly like this, and I have more than three elements. From this data, I want to create three files: x.json, y.json, and z.json. I want the contents of each of those files to be the contents of the paymentResponseObject.

Is there a way to do this with jq? I've figured out how to do this in awk, but it's very clunky and I want to be able to repeat this process with different schemas. I have to rewrite 80% of the awk script for each schema.

3 Answers 3

10

From this SO thread:

jq -cr 'keys[] as $k | "\($k)\n\(.[$k])"' input.json | while read -r key; do
  fname=$(jq --raw-output ".[$key].billingAccountNumber" input.json)
  read -r item
  printf '%s\n' "$item" > "./$fname"
done
2
  • Thanks. What does "\($k)\n\(.[$k])" do? Jul 27, 2018 at 19:44
  • NP. It prints the object key, a newline, and then the object itself.
    – jesse_b
    Jul 27, 2018 at 19:45
3

Give a try with this code to save each element with names 0.json, 1.json and 2.json. FYI, it can work for any number of JSON items in an array.

for i in $(seq $(jq '.|length' sample.json)); do \
    j=$( expr $i - 1 ); \
    jq ".[$j]" sample.json > $j.json;  \
done

Explanation:

below line finds the length of array to name the objects:

$(seq $(jq '.|length' sample.json))

Since the array starts with 0, lets fix the output file name

j=$( expr $i - 1 );

below lines fetches one element from json document and save to file

jq ".[$j]" sample.json > $j.json;

to save literally to x.json, y.json and z.json:

for i in x y z; do \
  jq ".[$count]" sample.json > $i.json;  \
  count=$( expr $count + 1 ) \
done
2
  • thanks @AdminBee for the edit/re-format; looks better now. Aug 30, 2021 at 17:01
  • 1
    All part of the service ;)
    – AdminBee
    Aug 31, 2021 at 8:44
1

Make jq create a shell script for you:

jq -r '.[] |
    @sh "cat >\(.billingAccountNumber).json <<\\END",
    .paymentResponseObject,
    "END"' file.json

This takes each top-level array element in your JSON document and creates a shell here-document redirection for each. Each redirection into cat is quoted to avoid that the shell expands things in the document. The output goes to the file named by the .billingAccountNumber value, quoted by the @sh formatter in jq.

Given the data in the question, this creates the script

cat >'x'.json <<\END
{
  "uiErrorDipslayMessage": "",
  "transactionStatus": "S",
  "transactionDescription": "",
  "transactionCode": "",
  "confirmationNumber": "1"
}
END
cat >'y'.json <<\END
{
  "uiErrorDipslayMessage": "",
  "transactionStatus": "S",
  "transactionDescription": "",
  "transactionCode": "",
  "confirmationNumber": "2"
}
END
cat >'z'.json <<\END
{
  "uiErrorDipslayMessage": "",
  "transactionStatus": "S",
  "transactionDescription": "",
  "transactionCode": "",
  "confirmationNumber": "3"
}
END

This could be run by sh to create the three files x.json, y.json, and z.json using the three embedded and quoted here-documents.

Running this would be a matter of piping it into sh -s directly from jq.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .