16

I have a large JSON file that is an object of objects, which I would like to split into separate files name after object keys. Is it possible to achieve this using jq or any other off-the-shelf tools?

The original JSON is in the following format

{ "item1": {...}, "item2": {...}, ...}

Given this input I would like to produce files item1.json, item2.json etc.

3
  • Do you want to convert it to different files are different variables? There are so many ways, with which you can convert into different variables. Commented Feb 26, 2015 at 14:26
  • 1
    I want to convert each object represented by its own key to a separate file. Is there any way to do it with jq or similar tools? Commented Feb 26, 2015 at 20:43
  • You can only generate one output at a time. Just make up a script that would get all the item names, then fork out jq calls to get those items out and save to a file. Commented Feb 26, 2015 at 21:29

3 Answers 3

17

This should give you a start:

for f in `cat input.json | jq -r 'keys[]'` ; do
  cat input.json | jq ".$f" > $f.json
done

or when you insist on more bashy syntax like some seem to prefer:

for f in $(jq -r 'keys[]') ; do
  jq ".[\"$f\"]" < input.json > "$f.json"
done < input.json
Sign up to request clarification or add additional context in comments.

4 Comments

To ensure proper behavior in the presence of any key name, it would be necessary to write ".[\"$f\"]" rather than ".$f"
cat file | command is anti-pattern, backticks is another one
jq --arg f "$f" '.[$f]' would be a further improvement, preventing arbitrary code injection (granted, not a big security issue until jq gets a system() call, or a way to open files for write, or other facilities that it doesn't currently have) by ensuring that the substituted value can only ever be treated as a literal string.
jq -n --argjson f "$f" '$f' will also guarantee a good parsing; -n is important if your not piping
10

Here's a solution that requires only one call to jq:

jq -cr 'keys[] as $k | "\($k)\n\(.[$k])"' input.json |
  while read -r key ; do
    read -r item
    printf "%s\n" "$item" > "/tmp/$key.json"
  done

It might be faster to pipe the output of the jq command to awk, e.g.:

jq -cr 'keys[] as $k | "\($k)\t\(.[$k])"' input.json |
  awk -F\\t '{ print $2 > "/tmp/" $1 ".json" }'

Of course, these approaches will need to be modified if the key names contain characters that cannot be used in filenames.

Comments

1

Is it possible to achieve this using jq or any other off-the-shelf tools?

It is. The command-line JSON-parser can help out.

Let's assume 'input.json':

{
  "item1": {
    "a": 1
  },
  "item2": {
    "b": 2
  },
  "item3": {
    "c": 3
  }
}

You could use Bash, as the other answers lay out...

$ for f in $(xidel -s "input.json" -e '$json()'); do
  xidel -s "input.json" --variable f="$f" -e '$json($f)' > "$f.json"
done

$ xidel -s "input.json" -e '$json() ! (.,serialize($json(.),{"method":"json"}))' |
  while read -r key ; do read -r item; printf "%s\n" "$item" > "$key.json"; done

...but with the integrated EXPath File Module Xidel can also do this very efficiently:

$ xidel -s "input.json" -e '
  $json() ! file:write(`{.}.json`,$json(.),{"method":"json"})
'

$ xidel -s item[123].json -e '$raw'
{"a":1}
{"b":2}
{"c":3}
$ xidel -s "input.json" -e '
  $json() ! file:write(`{.}.json`,$json(.),{"method":"json","indent":true()})
'

$ xidel -s item[123].json -e '$raw'
{
  "a": 1
}
{
  "b": 2
}
{
  "c": 3
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.