Display curl output in readable JSON format in Unix shell script - json

In my Unix shell script, when I execute a curl command, the result will be displayed as below which I am redirecting to file:
{"type":"Show","id":"123","title":"name","description":"Funny","channelTitle":"ifood.tv","lastUpdateTimestamp":"2014-04-20T20:34:59","numOfVideos":"15"}
But, I want this output to put in the readable JSON format like below in the file:
{"type":"Show",
"id":"123",
"title":"name",
"description":"Funny",
"channelTitle":"ifood.tv",
"lastUpdateTimestamp":"2014-04-20T20:34:59",
"numOfVideos":"15"}
How do I format the output this way?

A few solutions to choose from:
json json is a fast CLI tool for working with JSON. It is a single-file node.js script with no external deps (other than node.js itself).
$ echo '{"type":"Bar","id":"1","title":"Foo"}' | json
{
"type": "Bar",
"id": "1",
"title": "Foo"
}
Require:
# npm install -g json
json_pp: command utility available in Linux systems for JSON decoding/encoding
echo '{"type":"Bar","id":"1","title":"Foo"}' | json_pp -json_opt pretty,canonical
{
"id" : "1",
"title" : "Foo",
"type" : "Bar"
}
You may want to keep the -json_opt pretty,canonical argument for predictable ordering.
jq: lightweight and flexible command-line JSON processor. It is written in portable C, and it has zero runtime dependencies.
echo '{"type":"Bar","id":"1","title":"Foo"}' | jq '.'
{
"type": "Bar",
"id": "1",
"title": "Foo"
}
The simplest jq program is the expression ., which takes the input and produces it unchanged as output.
For additional jq options check the manual
python yq yq: Command-line YAML/XML/TOML processor - jq wrapper for YAML, XML, TOML documents
$ echo '{"type":"Bar","id":"1","title":"Foo"}' | yq
{
"type": "Bar",
"id": "1",
"title": "Foo"
}
The go version go yq doesn't work here
With xidel Command line tool to download and extract data from HTML/XML pages or JSON-APIs, using CSS, XPath 3.0, XQuery 3.0, JSONiq or pattern matching. It can also create new or transformed XML/HTML/JSON documents.
$ echo '{"type":"Bar","id":"1","title":"Foo"}' | xidel -e '$json'
{
"type": "Bar",
"id": "1",
"title": "Foo"
}
with python:
echo '{"type":"Bar","id":"1","title":"Foo"}' | python -m json.tool
{
"id": "1",
"title": "Foo",
"type": "Bar"
}
with nodejs and bash:
echo '{"type":"Bar","id":"1","title":"Foo"}' | node -e "console.log( JSON.stringify( JSON.parse(require('fs').readFileSync(0) ), 0, 1 ))"
{
"type": "Bar",
"id": "1",
"title": "Foo"
}

I am guessing that you want to prettify the JSON output.
That could be achieved using python:
curl http://localhost:8880/test.json | python -mjson.tool > out.json

This is to add to of Gilles' Answer. There are many ways to get this done but personally I prefer something lightweight, easy to remember and universally available (e.g. come with standard LTS installations of your preferred Linux flavor or easy to install) on common *nix systems.
Here are the options in their preferred order:
Python Json.tool module
echo '{"foo": "lorem", "bar": "ipsum"}' | python -mjson.tool
pros: almost available everywhere; cons: no color coding
jq (may require one time installation)
echo '{"foo": "lorem", "bar": "ipsum"}' | jq
cons: needs to install jq; pros: color coding and versatile
json_pp (available in Ubuntu 16.04 LTS)
echo '{"foo": "lorem", "bar": "ipsum"}' | json_pp
For Ruby users
gem install jsonpretty
echo '{"foo": "lorem", "bar": "ipsum"}' | jsonpretty

You can use the json node module:
npm i -g json
then simply append | json after curl.
curl http://localhost:8880/test.json | json

python -m json.tool
Curl http://127.0.0.1:5000/people/api.json | python -m json.tool
can also help.

You can install jq and make the query like below:
curl http://localhost:8080/observations/station/221 | jq

I found json_reformat to be very handy. So I just did the following:
curl http://127.0.0.1:5000/people/api.json | json_reformat
that's it!

Motivation: You want to print prettify JSON response after curl command request.
Solution: json_pp - commandline tool that converts between some input and output formats (one of them is JSON). This program was copied from json_xs and modified. The default input format is json and the default output format is json with pretty option.
Synposis:
json_pp [-v] [-f from_format] [-t to_format] [-json_opt options_to_json1[,options_to_json2[,...]]]
Formula: <someCommand> | json_pp
Example:
Request
curl -X https://jsonplaceholder.typicode.com/todos/1 | json_pp
Response
{
"completed" : false,
"id" : 1,
"title" : "delectus aut autem",
"userId" : 1
}

Check out curljson
$ pip install curljson
$ curljson -i <the-json-api-url>

With xidel:
curl <...> | xidel - -se '$json'
xidel can probably retrieve the JSON for you as well.

A lot more features (slice, filter and map and transform structured ) apart from formatting.
https://stedolan.github.io/jq/

Related

Shell - jq: Turning json list of `keys.childkeys` into list of lists

I am using jq to parse a json file. This is the current output from jq:
[
"key1.childk2",
"key2.childk3"
]
I would like to turn this into a readable json format itself as list of lists like below:
[
["key1","childk2"],
["key2","childk3"]
]
I would ideally prefer to do this with jq, however any other shell tool that can work on shell variables is fair game.
You can use jq split filter:
jq '[.[] | split(".")]'
[
[
"key1",
"childk2"
],
[
"key2",
"childk3"
]
]
You should use it somewhere in the original jq command
With jq you can structurally turn the input into your desired JSON document using simple filters like map(./"."), but regarding the requested readability, the output wouldn't have exactly your desired formatting.
Without any further flags, jq would pretty-print the output as:
[
[
"key1",
"childk2"
],
[
"key2",
"childk3"
]
]
Demo
Using the --compact-output or -c flag would compress the whole JSON document into one line, not just the elements of the outer array:
[["key1","childk2"],["key2","childk3"]]
Demo
So, if you really wanted to, you could also glue together the parts yourself as you like them to be formatted from within jq, but honestly, I would discourage you from doing so as by circumventing jq's internal JSON composer you might end up outputting invalid JSON.
jq -r '"[", " " + (map(./"." | tojson) | .[:-1][] += ",")[], "]"'
[
["key1","childk2"],
["key2","childk3"]
]
Demo
Here is a ruby to do that:
ruby -r json -e 'puts JSON.parse($<.read).map{|e| e.split(".")}.to_json' file
[["key1","childk2"],["key2","childk3"]]
Or if you want it pretty:
ruby -r json -e 'puts JSON.pretty_generate(
JSON.parse($<.read).map{|e| e.split(".")})
' file
[
[
"key1",
"childk2"
],
[
"key2",
"childk3"
]
]
Or you can produce your precise format:
ruby -r json -e '
l=[]
JSON.parse($<.read).map{|e| l << e.split(".").to_s}
puts "[\n\t#{l.join(",\n\t")}\n]"
' file
[
["key1", "childk2"],
["key2", "childk3"]
]

extract specific key value from json in bash if key matches

I have a json content (output.json)
{"project": {"id": "A", "content": [{"name": "XYZ", "location": "Berlin", "comments":""}, {"name": "ABC", "location": "NewYork", "comments": "Hwllo"}, {"name": "DEF", "location": "Paris", "comments": "Success"}]}}
I would like to extract location key with value when name matches say ABC from the above json using bash or shell commands
I tried something like below which gives be content within curly braces. but not sure on searching specific key.
cat output.json | grep -o -e "{.*}"
Output expectations:
if name matches ABC, get output as "location":"NewYork"
Any suggestions on processing further?
For extracting from json you should use jq if you can. According to authors "jq is like sed for JSON data" (source).
In your case it should be:
$ jq -r '.project' output.json | jq -r '.content' | jq '.[] | select(.name=="ABC")' | jq -r '.location'
Output will be:
NewYork
To get output which you required so:
"location":"NewYork"
You can use:
echo "\"location\":$(jq -r '.project' output.json | jq -r '.content' | jq '.[] | select(.name=="ABC")' | jq '.location')"
Before you use jq you should install it on Debian and Ubuntu it will be:
$ sudo apt install jq
for other OS you should check this site.
There may be better ways to do it, in a quick twisted way is here.
cat output.json | sed 's/"name"/\n"name"/g' | grep '"name"' | awk -F',' '{print $2}'
Add | grep <preferred name> also if need to filter based on name.
Use Perl
$ perl -0777 -lne ' while(/"name":\s+"ABC",\s+"location":\s+(\S+)/msg) { print "$1\n" } ' output.json
"NewYork",
$ cat output.json
{"project": {"id": "A", "content": [{"name": "XYZ", "location": "Berlin", "comments":""}, {"name": "ABC", "location": "NewYork", "comments": "Hwllo"}, {"name": "DEF", "location": "Paris", "comments": "Success"}]}}
$
Please use a JSON parser for processing JSON.
With Xidel it's as simple as:
xidel -s output.json -e '($json//content)()[name="ABC"]/location'
Alternatively:
xidel -s output.json -e '$json/(.//content)()[name="ABC"]/location'
or in full:
xidel -s output.json -e '$json/project/(content)()[name="ABC"]/location'
The above is XPath notation (example #11, Reading JSON). Dot notation (like jq) is also possible:
xidel -s output.json -e '($json).project.content()[name="ABC"].location'
[edit]
Commands above put out NewYork and I just realized you require the output to be "location":"NewYork". Xidel can do that too:
xidel -s output.json -e '($json//content)()[name="ABC"]/concat("""location"":""",location,"""")'
[/edit]

Why is my jq failing on my JSON?

Haven't used jq before but I'm wanting to build a shell script that will get a JSON response and extract just the values. To learn I thought I would try on my blog's WP API but for some reason I'm getting an error of:
jq: error (at :322): Cannot index array with string "slug"
When researching for and testing previous questions:
jq: Cannot index array with string
jq is sed for JSON
JSON array to bash variables using jq
How to use jq in a shell pipeline?
How to extract data from a JSON file
The above reading I've tried to code:
URL="http://foobar.com"
RESPONSE=$(curl -so /dev/null -w "%{http_code}" $URL)
WPAPI="/wp-json/wp/v2"
IDENTIFIER="categories"
if (("$RESPONSE" == 200)); then
curl -s {$URL$WPAPI"/"$IDENTIFIER"/"} | jq '.' >> $IDENTIFIER.json
result=$(jq .slug $IDENTIFIER.json)
echo $result
else
echo "Not returned status 200";
fi
An additional attempt changing the jq after the curl:
curl -s {$URL$WPAPI"/"$IDENTIFIER"/"} | jq '.' | $IDENTIFIER.json
result=(jq -r '.slug' $IDENTIFIER.json)
echo $result
I can modify the uncompress with the python JSON tool:
result=(curl -s {$URL$WPAPI"/"$IDENTIFIER"/"} | python -m json.tool > $IDENTIFIER.json)
I can save the JSON to a file but when I use jq I cannot get just the slug and here are my other trys:
catCalled=$(curl -s {$URL$WPAPI"/"$IDENTIFIER"/"} | python -m json.tool | ./jq -r '.slug')
echo $catCalled
My end goal is to try to use jq in a shell script and build a slug array with jq. What am I doing wrong in my jq and can I use jq on a string without creating a file?
Return from curl after uncompress per comment request:
[
{
"id": 4,
"count": 18,
"description": "",
"link": "http://foobar.com/category/foo/",
"name": "Foo",
"slug": "foo",
"taxonomy": "category",
},
{
"id": 8,
"count": 9,
"description": "",
"link": "http://foobar.com/category/bar/",
"name": "Bar",
"slug": "bar",
"taxonomy": "category",
},
{
"id": 5,
"count": 1,
"description": "",
"link": "http://foobar.com/category/mon/",
"name": "Mon",
"slug": "mon",
"taxonomy": "category",
},
{
"id": 11,
"count": 8,
"description": "",
"link": "http://foobar.com/category/fort/",
"name": "Fort",
"slug": "fort",
"taxonomy": "category",
}
]
eventually my goal is trying to get the name of the slug's into an array like:
catArray=('foo','bar','mon', 'fort')
There are 2 issues here:
slug is not a root level element in your example json. The root level element is an array. If you want to access the slug property of each element of the array, you can do so like this:
jq '.[].slug' $IDENTIFIER.json
Your example json has trailing commas after the last property of each array element. Remove the commas after "taxonomy": "category".
If I take your sample json, remove the errant commas, save it to a plain text file called test.json and run the following command:
jq '.[].slug' test.json
I get the following output:
"foo"
"bar"
"mon"
"fort"
Preprocessing
Unfortunately, the JSON-like data shown as having been produced by curl is not strictly JSON. jq does not have a "relaxed JSON" mode, so in order to use jq, you will have to preprocess the JSON-like data, e.g. using hjson (see http://hjson.org/):
$ hjson -j input.qjson > input.json
jq
With the JSON in input.json:
$ jq -c 'map(.slug)' input.json
["foo","bar","mon","fort"]
your string is not json, notice how the last member of your objects ends with a comma,
{foo:"bar",baz:9,}
this is legal in javascript, but it's illegal in json. if you are supposed to be receiving json from that endpoint, then contact the people behind it and tell them to fix the bug (it's breaking the json specs by ending objects's last member with a comma, which is illegal in json.) - until it's fixed, i guess you can patch it with a little regex, but it's a dirty quickfix, and probably not very reliable, but running it through
perl -p -0777 -e 's/\"\,\s*}/\"}/g;' makes it legal json..

Parse json into array in bash

{
"db_status": {
"sysa": {
"taskname": "AB",
"state": "Running",
"status": "System ATTENTION",
"updated": "0727",
"version": "5"
},
"sysb": {
"taskname": "null",
"state": "Standby",
"status": "System OK",
"updated": "0727",
"version": "6"
}
}
}
CURL command returns json object. Trying to get both state variables in an array i.e. running and standby. So far i have tried
curl -s http://localhost:9099/api | grep state | sed 's/"//g' | awk -F ": " '/state/ {print $2}' | tr '\n' ' ' | sed s'/..$//'
FYI you don't need a pipeline of 20 different commands when you're using awk. The command line you provided can be written as just one command:
awk -F'"' '$2=="state"{printf "%s%s", (++c>1?", ":""),$4}'
but all you really need is:
awk -F'"' '$2=="state"{print $4}'
and to save the output in a shell array (assuming your json is really always formatted as you show in your question) would be:
$ arr=( $(cat file | awk -F'"' '$2=="state"{print $4}') )
$ echo "${arr[0]}"
Running
$ echo "${arr[1]}"
Standby
Replace cat file with your curl command.
You could use jq
$ curl -s http://localhost:9099/api | jq '.db_status.sysa.state, .db_status.sysb.state'
"Running"
"Standby"
Or if you want all the entries no matter how many
$ curl -s http://localhost:9099/api | jq '.db_status[].state'
"Running"
"Standby"
Alternatively, if you don't have jq (or just looking for ways of feeling pain) you can parse most json in bash using ticktick -- which is written in 250 lines of bash

Find the value of key from JSON

I'd like to extract the "id" key from this single line of JSON.
I believe this can be accomplished with grep, but I am not sure on the correct way.
If there is a better way that does not have dependencies, I would be interested.
Here is my example output:
{
"data": {
"name": "test",
"id": "4dCYd4W9i6gHQHvd",
"domains": ["www.test.domain.com", "test.domain.com"],
"serverid": "bbBdbbHF8PajW221",
"ssl": null,
"runtime": "php5.6",
"sysuserid": "4gm4K3lUerbSPfxz",
"datecreated": 1474597357
},
"actionid": "WXVAAHQDCSILMYTV"
}
If you have a grep that can do Perl compatible regular expressions (PCRE):
$ grep -Po '"id": *\K"[^"]*"' infile.json
"4dCYd4W9i6gHQHvd"
-P enables PCRE
-o retains nothing but the match
"id": * matches "id" and an arbitrary amount of spaces
\K throws away everything to its left ("variable size positive look-behind")
"[^"]*" matches two quotes and all the non-quotes between them
If your grep can't do that, you an use
$ grep -o '"id": *"[^"]*"' infile.json | grep -o '"[^"]*"$'
"4dCYd4W9i6gHQHvd"
This uses grep twice. The result of the first command is "id": "4dCYd4W9i6gHQHvd"; the second command removes everything but a pair of quotes and the non-quotes between them, anchored at the end of the string ($).
But, as pointed out, you shouldn't use grep for this, but a tool that can parse JSON – for example jq:
$ jq '.data.id' infile.json
"4dCYd4W9i6gHQHvd"
This is just a simple filter for the id key in the data object. To get rid of the double quotes, you can use the -r ("raw output") option:
$ jq -r '.data.id' infile.json
4dCYd4W9i6gHQHvd
jq can also neatly pretty print your JSON:
$ jq . infile.json
{
"data": {
"name": "test",
"id": "4dCYd4W9i6gHQHvd",
"domains": [
"www.test.domain.com",
"test.domain.com"
],
"serverid": "bbBdbbHF8PajW221",
"ssl": null,
"runtime": "php5.6",
"sysuserid": "4gm4K3lUerbSPfxz",
"datecreated": 1474597357
},
"actionid": "WXVAAHQDCSILMYTV"
}
Just pipe your data to jq and select by keys
"data": {
"name": "test",
"id": "4dCYd4W9i6gHQHvd",
"domains": [
"www.test.domain.com",
"test.domain.com"
],
"serverid": "bbBdbbHF8PajW221",
"ssl": null,
"runtime": "php5.6",
"sysuserid": "4gm4K3lUerbSPfxz",
"datecreated": 1474597357
},
"actionid": "WXVAAHQDCSILMYTV"
} | jq '.data.id'
# 4dCYd4W9i6gHQHvd
Tutorial Here
I found myself that the best way is to use python, as it handles JSON natively and is preinstalled on most systems these days, unlike jq:
$ python -c 'import sys, json; print(json.load(sys.stdin)["data"]["id"])' < infile.json
4dCYd4W9i6gHQHvd
No python ,jq, awk, sed just GNU grep:
#!/bin/bash
json='{"data": {"name": "test", "id": "4dCYd4W9i6gHQHvd", "domains": ["www.test.domain.com", "test.domain.com"], "serverid": "bbBdbbHF8PajW221", "ssl": null, "runtime": "php5.6", "sysuserid": "4gm4K3lUerbSPfxz", "datecreated": 1474597357}, "actionid": "WXVAAHQDCSILMYTV"}'
echo $json | grep -o '"id": "[^"]*' | grep -o '[^"]*$'
Tested & working here: https://ideone.com/EG7fv7
source: https://brianchildress.co/parse-json-using-grep
$ grep -oP '"id": *"\K[^"]*' infile.json
4dCYd4W9i6gHQHvd
Hopefully it will work for all. As this will work for me to print without quotes.