JSON parse with jq and filter - json

I am learning jq. I have content as below:
{
"amphorae_links":[
],
"amphorae":[
{
"status":"BOOTING",
"loadbalancer_id":null,
"created_at":"2020-06-23T08:56:56",
"vrrp_id":null,
"id":"6d66935e-6d39-40c9-bb0d-dd6a734dc77b"
},
{
"status":"ALLOCATED",
"loadbalancer_id":"79970c9a-b0ba-4cde-a7e6-16b61641a7b8",
"created_at":"2020-06-25T06:41:56",
"vrrp_id":1,
"id":"872c08ee-9b21-4b26-9550-c2ffb4a1ad59"
}
]
}
I want to have an output like
"ALLOCATED=872c08ee-9b21-4b26-9550-c2ffb4a1ad59,79970c9a-b0ba-4cde-a7e6-16b61641a7b8"
I tried to use below, but I don't know how to remove the line with status as "BOOTING".
.amphorae[] | "\(.status)=\(.id),\(.loadbalancer_id)"
Thanks anyone for the helping.

You have a couple of options:
You can specify the second index of the amphorae array, that is if you are sure the "ALLOCATED" part will always be the second index of the amphorae array
.amphorae[1] | "\(.status)=\(.id),\(.loadbalancer_id)"
This is a little bit more complex, but will always ensure you get only the element of the amphorae array where the status key is 'ALLOCATED'
.amphorae | map(select(.status == "ALLOCATED"))[] | "\(.status)=\(.id),\(.loadbalancer_id)"

Related

JSON: using jq with variable keys

I have input JSON data in a bunch of files, with an IP address as one of the keys. I need to iterate over a the files, and I need to get "stuff" out of them. The IP address is different for each file, but I'd like to use a single jq command to get the data. I have tried a bunch of things, the closest I've come is this:
jq '.facts | keys | keys as $ip | .[0]' a_file
On my input in a_file of:
{
"facts": {
"1.1.1.1": {
"stuff":"value"
}
}
}
it returns the IP address, i.e. 1.1.1.1, but then how do I to go back do something like this (which is obviously wrong, but I hope you get the idea):
jq '.facts.$ip[0].stuff' a_file
In my mind I'm trying to populate a variable, and then use the value of that variable to rewind the input and scan it again.
=== EDIT ===
Note that my input was actually more like this:
{
"facts": {
"1.1.1.1": {
"stuff": {
"thing1":"value1"
}
},
"outer_thing": "outer_value"
}
}
So I got an error:
jq: error (at <stdin>:9): Cannot index string with string "stuff"
This fixed it- the question mark after .stuff:
.facts | keys_unsorted[] as $k | .[$k].stuff?
You almost got it right, but need the object value iterator construct, .[] to get the value corresponding to the key
.facts | keys_unsorted[] as $k | .[$k].stuff
This assumes that, inside facts you have one object containing the IP address as the key and you want to extract .stuff from it.
Optionally, to guard against objects that don't contain stuff inside, you could add ? as .[$k].stuff?. And also you could optionally validate keys against a valid IP regex condition and filter values only for those keys.

Add a new entry to a Json after or before a specified existing entry with jq

I have the following input and I want to below output using jq.
I would like to add
an entry "zeiterfassungAktiviert" : truejust after the key gueltigBis (or alternatively before the key inhaltsverzeichnis)
add an entry to the end of the object inhaltsverzeichnis by adding the entry "zeiterfassung": "zeiterfassung"
Example input:
{
"fachbereich": "qp",
"produktTyp": "PRODUKT_ANFRAGE_V1",
"name": "Produkt Anfrage",
"kurzName": "anfrage",
"gueltigAb": "2019-01-01T00:00:00.000",
"gueltigBis": "2022-12-31T00:00:00.000",
"inhaltsverzeichnis": {
"versandumfang": "auftragsverwaltung/versandumfang",
"dokumentenerzeugung": "dokumentenerzeugung"
}
}
Example output:
{
"fachbereich": "qp",
"produktTyp": "PRODUKT_ANFRAGE_V1",
"name": "Produkt Anfrage",
"kurzName": "anfrage",
"gueltigAb": "2019-01-01T00:00:00.000",
"gueltigBis": "2022-12-31T00:00:00.000",
"zeiterfassungAktiviert": true,
"inhaltsverzeichnis": {
"versandumfang": "auftragsverwaltung/versandumfang",
"dokumentenerzeugung": "dokumentenerzeugung",
"zeiterfassung": "zeiterfassung"
},
}
I managed to do the second part but am not clear about how to go about the first part.
Command: jq '.zeiterfassungAktiviert += "zeiterfassung" | .inhaltsverzeichnis.zeiterfassung += "zeiterfassung"'
The result is as follows:
{
"fachbereich": "qp",
"produktTyp": "PRODUKT_ANFRAGE_V1",
"name": "Produkt Anfrage",
"kurzName": "anfrage",
"gueltigAb": "2019-01-01T00:00:00.000",
"gueltigBis": "2022-12-31T00:00:00.000",
"inhaltsverzeichnis": {
"versandumfang": "auftragsverwaltung/versandumfang",
"dokumentenerzeugung": "dokumentenerzeugung",
"zeiterfassung": "zeiterfassung"
},
"zeiterfassungAktiviert": true
}
As you can see it is added to the end of the root object. I would like to specify the position somehow, ideally without having to convert into array and convert back if possible but rather by saying please add entry after/before a specified key.
For the first part, it helps to have a helper function:
def insertkv($afterkey; $key; $value):
def insertafter($ix; $x): .[0:1+$ix] + [$x] + .[1+$ix:];
to_entries
| insertafter( map(.key) | index($afterkey); {$key, $value})
| from_entries;
Versions
The above is intended for use with jq 1.5 or later. Some minor fiddling is required for earlier versions.

jq - Selecting objects containing certain key

Let's say I have this JSON file below:
{
"team": {
"money": 100,
},
"group": {
"money": 200,
"snack": true,
}
}
I want to select the objects which has a "snack" key including its parent. The current command I'm using is:
jq '..|objects|select(has("snack"))' json
This however, does not include the parent, which in this case is "group". How do I select the parent of the selected object as well?
Instead of using .., you could use paths. That is, you'd select the paths that lead to the items of interest, and work from there. So you'd start with:
paths(objects) as $p
| select(getpath($p)|has("snack"))
| $p
For the given input (after having been corrected), this would yield:
["group"]
So you might want to replace the $p in the last line by $p[-1], but it's not altogether clear how useful that would be. More useful would be getpath( $p[:-1] )

How to return a child node and a parent node attribute in the same query JMES Path for JSON object

Considert we have this JSON:
{
"A":[
{
"AT":"text"
},
{
"AT":"text2"
}
],
"B":[
{
"name":"text",
"power":10
},
{
"name":"text1",
"power":20
},
{
"name":"text2",
"power":40
}
]
}
I want to return "AT" and the power corresponding to "AT", so I don't want to return "text1" but just [["text",10],["text2",40]]. This query [A[*].AT,B[*].power] return this: [["text","text2"],[10,20,40]]. So it's not exactly what I want because I don't want the extra value: 20 from "text1". How can I fix that ?
EDIT: I found this portion of code #.B[?contains(['text','text2'],name)].[name,power][][] it return ["text",10,"text2",40] this is the good result BUT the query isn't dynamics, because in the contain I have this ['text','text2'] and I want to replace this portion of code with a nested query if it's possible, so, how can I do that ?
I don't think it's possible because as I know we can't store variables in JMES Path ad we can't use the current-node(#) in array filter as #.B[?contains(#[].A[].AT,name)].[name,power][][] . Apparently JMES Path doesn't know to retrieve informations from node A once it get into the node B. You should use jq, with this query:
( .B | map({(.name): .power}) | add ) as $b | .A | map(.AT | [., $b[.]]) | add

how to filter array and slice results with jq

Suppose I have the following data:
{
"dashboards": [
{
"name": "first",
"type": "standard"
},
{
"name": "second",
"type": "custom"
}
]
}
(actually there's a lot more data than that, I am just showing what the structure of the data is)
What I am trying to do is get the first 10 dashboards of type standard.
I know I can get all the standard dashboards with:
jq '.dashboards[] | select(.type == "standard")'
But I can't figure out how to slice the resulting array...
If you want the result as an array, you could use map:
.dashboards | map(select(.type=="standard")) | .[0:10]
However, this is inefficient. For efficiency, it would be better to use limit as discussed below.
If you wanted the items as a stream, you could write:
limit(10; .dashboards[] | select(.type=="standard"))
If you want the results as an array, simply wrap the above jq expression in square brackets.