2

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?

1
  • For extracting from json you should use jq if you can. According to authors "jq is like sed for JSON data". Commented Jan 18, 2019 at 13:20

4 Answers 4

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. Looks interesting, gonna try now :)
1

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.

4 Comments

Thank you. This is good. but unfortunately, key location is not always in second place. awk with $2 is like hardcoded.
ohh .. ok then we can add one more sed before that, like cat output.json | sed 's/"name"/\n"name"/g' | grep '"name"' | sed 's/"location"/\n"location"/g' | awk -F',' '{print $1}' ... ;)
When I do this... I get both name and location key value pair. I still need to use tail -1 at the end which doesnt look good when the order is different
Yea its true ... ;)
0

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"}]}}
$

1 Comment

glad to hear that!.. please consider accepting the answer
0

Tools like grep (using RegEx) don't understand JSON. Please use a dedicated parser like instead:

$ xidel -s output.json \
  -e '$json/project/(content)()[name="ABC"]/location'        # XPath-like notation
  -e '$json/(.//content)()[name="ABC"]/location'
  -e '($json//content)()[name="ABC"]/location'
  -e '($json).project.content()[name="ABC"].location'               # dot notation
  -e '($json)("project")("content")()[name="ABC"]("location")'   # JSONiq notation
  -e '$json?project?content?*[name="ABC"]?location'             # XPath 3.1 syntax

If you literally require the output to be "location":"NewYork":

$ xidel -s output.json \
  -e '($json//content)()[name="ABC"]/concat("""location"":""",location,"""")'
  -e '($json//content)()[name="ABC"]/("""location"":"""||location||"""")'
  -e '($json//content)()[name="ABC"]/`"location":"{location}"`'

The last variant, with the string between backticks (`...`) is an XPath 4.0 String Template where everything between curly brackets ({...}) is evaluated.

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.