Flattening a nested json while printing outer json using jq - jq

I have a file like below (excerpt)
cat input.json
{
"Metrics": [
{
"Namespace": "CWAgent",
"MetricName": "disk_used_percent",
"Dimensions": [
{
"Name": "path",
"Value": "/aem"
},
{
"Name": "host",
"Value": "uat2-author01.uat.cloud.abc.com.au"
},
{
"Name": "device",
"Value": "mapper/aem-aem"
},
{
"Name": "fstype",
"Value": "ext4"
}
]
},
{
"Namespace": "CWAgent",
"MetricName": "mem_used_percent",
"Dimensions": [
{
"Name": "host",
"Value": "uat2-dispatch02.uat.cloud.abc.com.au"
}
]
}
]
}
As you can see the "Dimensions" is a list of json objects of varying length. I am required to print each objects under "Metrics" in a single line like below -
CWAgent|disk_used_percent|path:/aem|host:uat2-author01.uat.cloud.abc.com.au|device:mapper/aem-aem|fstype:ext4
CWAgent|mem_used_percent|host:uat2-dispatch02.uat.cloud.abc.com.au
I used this -
cat input.json | jq -r ".Metrics[] | .Namespace + \"|\" + .MetricName + \"|\" + (.Dimensions[] | .Name + \":\" + .Value + \"|\")"
And the output is like below:
CWAgent|disk_used_percent|path:/aem|
CWAgent|disk_used_percent|host:uat2-author01.uat.cloud.abc.com.au|
CWAgent|disk_used_percent|device:mapper/aem-aem|
CWAgent|disk_used_percent|fstype:ext4|
CWAgent|mem_used_percent|host:uat2-dispatch02.uat.cloud.abc.com.au|
As you can see, we are getting 4 lines for first object each having different object under Dimensions. Can anyone please suggest what will be the jq command to get desired output. I am novice in jq and wasted too much time on this experimenting.
Thanks,
Bijitesh

You're looking for this:
jq -r '.Metrics[] | [.Namespace, .MetricName] + (.Dimensions | map("\(.Name):\(.Value)")) | join("|")' input.json

Related

JQ does not contain for "key": "value" JSON structure

The question is similar to THIS, but the JSON structure is different.
In my usecase JSON has an arrays with key:value data:
{
"data":[
{
"name":"banana",
"tags":[
{
"id":"yellow"
},
{
"id":"long"
}
]
},
{
"name":"apple",
"tags":[
{
"id":"red"
},
{
"id":"round"
}
]
},
{
"name":"orange",
"tags":[
{
"id":"orange"
},
{
"id":"round"
},
{
"id":"colored"
}
]
}
]
}
What required is to filter the only elements that do not have certain keyword - "red" for instance.
When i use jq '.data[] | select(.tags[].id | index( "red" ))' it brings me the correct resut of 'apple' (as it has "id": "red") and 'orange' (as it has "id": "colored").
However, when i add the negation jq '.data[] | select(.tags[].id | index( "red" ) | not)' the results are more than strange, with elements' duplication, totally enigmatic.
How can i use jq to filter the result the way it returns only elements that do not have the exact match among the array values?
You can use all to give a condition all items must meet. Here, all values in .tags[].id must be unequal != to "red":
jq '.data[] | select(all(.tags[].id; . != "red"))'
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
Add .name and use the -r option to only get the names:
jq -r '.data[] | select(all(.tags[].id; . != "red")).name'
banana
orange
Demo
index works on an array, your current filter does not pass index to an array, therefore you're getting other results then expected.
.data[] | select([ .tags[].id ] | index("red") | not)
Here we create an array with all the id's [ .tags[].id ] and use that array to check for red: | index("red") | not
The above filter gives the following output:
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
If you want to exclude 'colored', use contains():
.data[] | select([ .tags[].id ] | contains(["red"]) | not)
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
Demo

jq query to pull values from json file to create a listing

Given this input small sample:
{
"_format_version": "1.1",
"_workspace": "test",
"services": [
{
"connect_timeout": 60000,
"host": "host-name-test.com",
"name": "name-of-service",
"path": "/test/oauthpass",
"port": 777,
"protocol": "http",
"read_timeout": 1000,
"retries": 1,
"write_timeout": 1000,
"routes": [
{
"hosts": [
"Google.com"
],
"name": "com.testing.active.oauth",
"methods": [
"POST"
],
"paths": [
"/vendors/otest/pass/?$"
],
"path_handling": "v8",
"preserve_host": false,
"protocols": [
"https"
],
"regex_priority": 0,
"strip_path": true,
"https_redirect_status_code": 426,
"request_buffering": true,
"response_buffering": true
}
]
}
}
trying to get a listing from the data pulling certain values like the listing below:
host-name-test.com, Google.com, POST, HTTPS
the command that I have working so far is
cat /tmp/petecar.json | jq -r ' .services[] | .routes[] | ( .hosts[] + "/" + .paths[]) ' | more
but I can't access the values under services, please provide some sample on how to get the values
routes has an array value and as such cannot be concatenated with a string. You can use join to turn that array into a single string:
jq -r '.services[] | .host + " " + (.routes[].hosts | join(","))'
Output:
host-name-test.com Google.com
Alternatively, using string interpolation) which will automatically serialize any values into their string representation:
jq -r '.services[] | "\(.host) \(.routes[].hosts)"'
Output:
host-name-test.com ["Google.com"]
join and string interpolation can be combined, giving you the identical output of the first command:
jq -r '.services[] | "\(.host) \(.routes[].hosts|join(","))"'

Parsing on jq with_entries

I have the following data
Command output:
| jq '.rrsets[]'
{
"comments": [],
"name": "name1.",
"records": [
{
"content": "10.10.10.10",
"disabled": false
}
],
"ttl": 60,
"type": "A"
}
{
"comments": [],
"name": "name2.",
"records": [
{
"content": "20.20.20.20",
"disabled": false
}
],
"ttl": 60,
"type": "CNAME"
}
I want to get names where type is A.
Help, tell me how to do this?
| jq '.rrsets[] | with_entries(select(.key== "name", .value == "A"))'
{
"name": "name1."
}
{
"name": "name2.",
"type": "A"
}
Displays all the lines, but I only need where type = A
I'm not sure if this is what you are looking for, but wouldn't simply ... | select(.type == "A") do the trick?
... | jq '.rrsets[] | select(.type == "A")'
{
"comments": [],
"name": "name1.",
"records": [
{
"content": "10.10.10.10",
"disabled": false
}
],
"ttl": 60,
"type": "A"
}
Demo
And then just get the .name if you want only that (using -r to get rid of the JSON formatting):
... | jq -r '.rrsets[] | select(.type == "A").name'
name1.
Demo

JQ: How do I extract item which has part of subitem value of "FOO" [duplicate]

I have a JSON file that looks like this:
{
"InstanceId": "i-9KwoRGF6jbhYdZi823aE4qN",
"Tags": [
{
"Key": "blah",
"Value": "server-blah"
},
{
"Key": "environment",
"Value": "ops"
},
{
"Key": "server_role",
"Value": "appserver"
},
{
"Key": "Name",
"Value": "some_name"
},
{
"Key": "product",
"Value": "some_server"
}
]
}
{
...more objects like the above...
}
I need to display the InstanceId where "Key" == "environment" and "Value" == "ops".
I have jq-1.6.
If I say:
cat source.json | jq '
{ InstanceId, Tags } |
(.Tags[] | select( .Key == "environment" ))
'
I get some of what I want, but I cannot figure out how to include InstanceId in the output nor how to incorporate the "and" part of the select.
Here is a simple but efficient approach using any:
select( any(.Tags[]; .Key=="environment" and .Value == "ops") )
| .InstanceId
An alternative approach that avoids .Tags[]:
{"Key": "environment", "Value": "ops"} as $object
| select( .Tags | index($object) )
| .InstanceId
I'm not sure if this is the exact output you're looking for (comment if it isn't), but this will output the InstanceIds of JSON objects that contain a Tag with Key environment and Value ops.
jq 'select( .Tags[] | (.Key == "environment" and .Value == "ops")) | .InstanceId' < source.json

Create an object with specified indexes

I am trying to use for loop for every object using jq.
Sample Input generated by Elasticsearch
{
"took": 202,
"timed_out": false,
"aggregations": {
"aggsDateHistogram": {
"buckets": [
{
"key": 1465974236000,
"search": {
"value": 14
}
},
{
"key": 1465975137000,
"search": {
"value": 16
}
}
]
}
}
}
I want to have an object that has a key value and corresponding value of value index from search.
{ "date": .aggregations.aggsDateHistogram.buckets[].key, "value": .aggregations.aggsDateHistogram.buckets[].search.value }
This gives me an object but with cartesian product, but I only want to have values like
key[1] : search[1].value
key[2] : search[2].value
So you want to produce this output?
[
{
"key": 1465974236000,
"value": 14
},
{
"key": 1465975137000,
"value": 16
}
]
The following will do just that:
.aggregations[].buckets
| map({key: .key, value: .search.value})
And from a terminal:
jq '.aggregations[].buckets
| map({key: .key, value: .search.value})' input.json
Here is a slightly simpler solution
[ .aggregations[].buckets[] | {key, value:.search.value} ]

Resources