Get parent element id while parsing json data with jq - json

I want to print ID of parent element when child element value is client_release from JSON data.
if
data.properties.value== "client_release"
then output should be
abcd1g2f,hirk5d7b3l
I tried below, but no luck
jq '.data[].properties[]|select(.value=="client_release")|.id'
JSON data is below:
{
"data":[
{
"id":"abcd1g2f",
"resourceURI":"https://somerepo.com/service/local/privileges/abcd1g2f",
"name":"release1",
"description":"release1",
"type":"target",
"userManaged":true,
"properties":[
{
"key":"repositoryGroupId",
"value":""
},
{
"key":"method",
"value":"create,read"
},
{
"key":"repositoryId",
"value":"client_release"
},
{
"key":"repositoryTargetId",
"value":"1"
}
]
},
{
"id":"asdf1k4g",
"resourceURI":"https://somerepo.com/service/local/privileges/asdf1k4g",
"name":"release2",
"description":"release2",
"type":"target",
"userManaged":true,
"properties":[
{
"key":"repositoryGroupId",
"value":""
},
{
"key":"method",
"value":"read"
},
{
"key":"repositoryId",
"value":"formal_release"
},
{
"key":"repositoryTargetId",
"value":"1"
}
]
},
{
"id":"hirk5d7b3l",
"resourceURI":"https://somerepo.com/service/local/privileges/hirk5d7b3l",
"name":"release3",
"description":"release3",
"type":"target",
"userManaged":true,
"properties":[
{
"key":"repositoryGroupId",
"value":""
},
{
"key":"method",
"value":"create,read"
},
{
"key":"repositoryId",
"value":"client_release"
},
{
"key":"repositoryTargetId",
"value":"1"
}
]
}
]
}

The idea is right, but the data[] array should be outside the select statement,
jq '.data[] | select(.properties[].value == "client_release") | .id'
To put it in the CSV format as indicated in the question, put the result into an array and use the #csv construct
jq --raw-output '[.data[] | select(.properties[].value == "client_release") | .id] | #csv'

The following filter avoids duplications and might be more efficient than using select(.properties[].value ...):
.data
| map(select(.properties | any(.[]; .value == "client_release")) | .id)
| join(",")
(You could alternatively use #csv at the end if you want the values of .id as JSON strings.)
"repositoryId"
If attention should only be paid to the value corresponding to "repositoryId", then you could
use from_entries, e.g.:
.data
| map(select(.properties | from_entries.repositoryId == "client_release") | .id)
| join(",")

Related

jq: map arrays to csv field headers

Is there a way to export a json like this:
{
"id":"2261026",
"meta":{
"versionId":"1",
"lastUpdated":"2021-11-08T15:13:39.318+01:00",
},
"address": [
"string-value1",
"string-value2"
],
"identifier":[
{
"system":"urn:oid:2.16.724.4.9.20.93",
"value":"6209"
},
{
"system":"urn:oid:2.16.724.4.9.20.2",
"value":"00042"
},
{
"system":"urn:oid:2.16.724.4.9.20.90",
"value":"UAB2"
}
]
}
{
"id":"2261027",
"meta":{
"versionId":"1",
"lastUpdated":"2021-11-08T15:13:39.318+01:00",
},
"address": [
"string-value1",
"string-value2",
"string-value3",
"string-value4"
],
"identifier":[
{
"system":"urn:oid:2.16.724.4.9.20.93",
"value":"6205"
},
{
"system":"urn:oid:2.16.724.4.9.20.2",
"value":"05041"
}
]
}
I'd like to get something like this:
"id","meta_versionId","meta_lastUpdated","address","identifier0_system","identifier0_value","identifier1_system","identifier1_value","identifier2_system","identifier2_value"
"2261026","1","2021-11-08T15:13:39.318+01:00","string-value1|string-value2","urn:oid:2.16.724.4.9.20.93","6209","urn:oid:2.16.724.4.9.20.2","00042","urn:oid:2.16.724.4.9.20.90","UAB2"
"2261027","1","2021-11-08T15:13:39.318+01:00","string-value1|string-value2|string-value3|string-value4","urn:oid:2.16.724.4.9.20.93","6205","urn:oid:2.16.724.4.9.20.2","05041",,
In short:
address array field string values has to be mapped joining its values using "|" character. Example: "string-value1|string-value2"
identifiers array field objects have to be mapped to "n-field-header". Example: "identifier0_system","identifier0_value","identifier1_system","identifier1_value","identifier2_system","identifier2_value,..."
Any ideas?
Try this
jq -r '[
.id,
(.meta | .versionId, .lastUpdated),
(.address | join("|")),
(.identifier[] | .system, .value)
] | #csv'
Demo
To prepend a header row with the number of identifierX_system and identifierX_value field pairs in it matching the length of the input's longest identifier array, try this
jq -rs '[
"id",
"meta_versionId", "meta_lastUpdated",
"address",
(
range([.[].identifier | length] | max)
| "identifier\(.)_system", "identifier\(.)_value"
)
], (.[] | [
.id,
(.meta | .versionId, .lastUpdated),
(.address | join("|")),
(.identifier[] | .system, .value)
]) | #csv'
Demo

JQ - Join nested arrays and filter

I'm trying to use JQ to create the following paths:
/staging-data-0/cassandra/cassandra-client-port
/staging-data-0/cassandra/cassandra-gossip-port
from the following blob of JSON (I've stripped unnecessary bits out):
{
"DebugConfig": {
"ServerPort": 8300,
"Services": [
{
"Checks": [
{
"CheckID": "cassandra-client-port",
"Timeout": "1s"
},
{
"CheckID": "cassandra-gossip-port",
"Timeout": "1s"
}
],
"Name": "cassandra"
},
{
"Checks": [
{
"CheckID": "cockroachdb-tcp",
"Timeout": "1s"
}
],
"Name": "cockroachdb"
}
]
},
"Member": {
"Name": "staging-data-0"
},
"Meta": {
"consul-network-segment": ""
}
}
I'm struggling with the JQ manual to generate the paths, I can only pull out the last part so far with
jq '.DebugConfig.Services | map(select(.Name=="cassandra")) | map(.Checks[].CheckID)'
The final path should be /{.Member.Name}/{.DebugConfig.Services.Name}/{.DebugConfig.Services.Checks.CheckID}
Only cassandra
jq -r '{a:.Member.Name, b:.DebugConfig.Services[]} | select(.b.Name=="cassandra") | {a:.a, b:.b.Name, c:.b.Checks[].CheckID} | [.a, .b, .c] | join("/")'
staging-data-0/cassandra/cassandra-client-port
staging-data-0/cassandra/cassandra-gossip-port
Both
jq -r '{a:.Member.Name, b:.DebugConfig.Services[]} | {a:.a, b:.b.Name, c:.b.Checks[].CheckID} | [.a, .b, .c] | join("/")'
staging-data-0/cassandra/cassandra-client-port
staging-data-0/cassandra/cassandra-gossip-port
staging-data-0/cockroachdb/cockroachdb-tcp
With your input, the jq filter:
.DebugConfig.Services[] as $s
| "/\(.Member.Name)/\($s.Name)/\($s.Checks[].CheckID)"
produces:
"/staging-data-0/cassandra/cassandra-client-port"
"/staging-data-0/cassandra/cassandra-gossip-port"
"/staging-data-0/cockroachdb/cockroachdb-tcp"
Since you only want the "cassandra" strings, you just need to interject a "select" filter:
.DebugConfig.Services[] as $s
| "/\(.Member.Name)/\($s.Name)/" +
($s
| select(.Name == "cassandra")
| .Checks[].CheckID)
but it's worth noting how easy it is to process all the "Checks" items.

JSON/JQ: Merge 2 files on key-value with condition

I have 2 JSON files. I would like to use jq to take the value of "capital" from File 2 and merge it with File 1 for each element where the same "name"-value pair occurs. Otherwise, the element from File 2 should not occur in the output. If there is no "name"-value pair for an element in File 1, it should have empty text for "capital."
File 1:
{
"countries":[
{
"name":"china",
"continent":"asia"
},
{
"name":"france",
"continent":"europe"
}
]
}
File 2:
{
"countries":[
{
"name":"china",
"capital":"beijing"
},
{
"name":"argentina",
"capital":"buenos aires"
}
]
}
Desired result:
{
"countries":[
{
"name":"china",
"continent":"asia",
"capital":"beijing"
},
{
"name":"france",
"continent":"europe",
"capital":""
}
]
}
You could first construct a dictionary from File2, and then perform the update, e.g. like so:
jq --argfile dict File2.json '
($dict.countries | map( {(.name): .capital}) | add) as $capitals
| .countries |= map( .capital = ($capitals[.name] // ""))
' File2.json
From a JSON-esque perspective, it would probably be better to use null for missing values; in that case, you could simplify the above by omitting // "".
Using INDEX/2
If your jq has INDEX/2, then the $capitals dictionary could be constructed using the expression:
INDEX($dict.countries[]; .name) | map_values(.capital)
Using INDEX makes the intention clearer, but if efficiency were a major concern, you'd probably be better off using reduce explicitly:
reduce $dict.countries[] as $c ({}; . + ($c | {(.name): .capital}))
One way:
$ jq --slurpfile file2 file2.json '
{ countries:
[ .countries[] |
. as $curr |
$curr + { capital: (($file2[0].countries[] | select(.name == $curr.name) | .capital) // "") }
]
}' file1.json
{
"countries": [
{
"name": "china",
"continent": "asia",
"capital": "beijing"
},
{
"name": "france",
"continent": "europe",
"capital": ""
}
]
}
An alternative:
$ jq -n '{ countries: ([inputs] | map(.countries) | flatten | group_by(.name) |
map(select(.[] | has("continent")) | add | .capital //= ""))
}' file[12].json

jq transform JSON child array objects to delimited string

I want to transform the following input with jq:
{
"root":[
{
"field1":"field1value1",
"field2":"field2value2",
"field3Array":[
{
"prop1":"prop1_value1"
}
]
},
{
"field1":"field1value3",
"field2":"field2value4",
"field3Array":[
{
"prop1":"prop1_value3"
},
{
"prop1":"prop1_value4"
}
]
}
]
}
Output should be:
[
{
"field1": "field1value1",
"field2": "field2value2",
"field3Array": "prop1_value1"
},
{
"field1": "field1value3",
"field2": "field2value4",
"field3Array": "prop1_value3,prop1_value4"
}
]
I use this jq filter so far:
[.root[] | {field1, field2, field3Array: .field3Array[].prop1}]
but I don't know how to join the array property "prop1" to a comma-delimited string "prop1_value3,prop1_value4".
https://jqplay.org/s/CR8mGBX8Dz
You need to map the objects contained in the field3Array to their string values and join the resulting array :
.root | map({field1, field2, field3Array: .field3Array | map(.prop1) | join(",")})
You can try it here.
It can be somewhat simplified in the following where we update the .field3Array in-place instead of recreating a whole object :
.root | map(.field3Array |= (map(.prop1) | join(",")))
You can try it here.
If you're unfamiliar with the map function, the following would have worked as well :
[.root[] | {field1, field2, field3Array: [ .field3Array[] | .prop1 ] | join(",")}]
You can try it here.

Json search and print

I've been trying to use jq parser to help me extract information from json files.
Here is an example snippet
{
"main_attribute": {
"name": {
"display_name": "abc"
},
"address": {
"unit": "1",
"street": "Dundas",
"suburb": "Syd",
"state": "NSW"
},
"financial_debt": {
"bank_loan": true
}
},
"secondary_attr": {
"income": {
"pretax": 100000
},
"automobile": {
"make": "Citroen",
"model": 2015,
"new": true
},
"property": {
"property_owned": 1,
"owned_since": 2000,
"first_sale": true
},
"education": {
"degree": "MS",
"graduated": 1990,
"financial_debt": {
"bank_loan": false
}
}
}
}
I need to find the blocks where "financial_debt" is true. This field could be either in the main_attribute (as a global value) or in the secondary attribute.
Expected output:
financial_debt: bank_loan on "automobile" and "property"
Can you please advise how to go about doing this search using jq?
This is by no means the most efficient way, but it is functional. It returns a boolean value specifying whether or not there is a true boolean value under the financial_debt property.
jq '[recurse | .financial_debt? | select(. != null) | recurse | booleans] | any'
tostream can be used to find paths containing "financial_debt" as follows:
tostream
| select(length==2)
| select(.[0] | contains(["financial_debt"]))
with this filter in filter.jq and data in data.json
$ jq -M -c -f filter.jq data.json
produces
[["main_attribute","financial_debt","bank_loan"],true]
[["secondary_attr","education","financial_debt","bank_loan"],false]
This intermediate result can be used along with reduce, setpath, getpath and a filter such as
. as $d
| reduce ( tostream
| select(length==2)
| select(.[0] | contains(["financial_debt"]))) as [$p,$v] (
{}
; setpath($p[:-1]; $d | getpath($p[:-1]))
)
to produce
{
"main_attribute": {
"financial_debt": {
"bank_loan": true
}
},
"secondary_attr": {
"education": {
"financial_debt": {
"bank_loan": false
}
}
}
}