jq: map object if field exists - jq

This is my jq code:
def pick_nationality:
{nation: {country: .NACIONALITAT, code: "some code"} };
def pick_surname:
{name: {surname: .SURNAME, code: "some code"} };
map([pick_nationality, pick_surname])
Problem here appears when some of .NACIONALITAT or .SURNAME is not present on input objects:
{
"SURNAME": "surname1"
}
{
"NACIONALITAT": "nacionalitat1"
}
Result:
[
[
{
"nation": {
"country": null,
"code": "some code"
}
},
{
"name": {
"surname": "surname1",
"code": "some code"
}
}
],
[
{
"nation": {
"country": "nacionalitat1",
"code": "some code"
}
},
{
"name": {
"surname": null,
"code": "some code"
}
}
]
]
Problem is I need to avoid pick_natinality when .NATIONALITAT field is not present...
Desired result would be:
[
[
{
"name": {
"surname": "surname1",
"code": "some code"
}
}
],
[
{
"nation": {
"country": "nacionalitat1",
"code": "some code"
}
}
]
]
Any ideas?

Just have the functions return nothing when the relevant field is missing.
def pick_nationality:
select(.NACIONALITAT) |
{nation: {country: .NACIONALITAT, code: "some code"} };
def pick_surname:
select(.SURNAME) |
{name: {surname: .SURNAME, code: "some code"} };
map([pick_nationality, pick_surname])
Demo on jqplay

You can use del for the respective null values such as
jq -r 'del(.[][] | select(.nation.country == null and .name.surname== null))'
Demo

You can add a test before capture the value :
def pick_nationality:
if has("NACIONALITAT")
then {nation: {country: .NACIONALITAT, code: "some code"} }
else empty end;
def pick_surname:
if has("SURNAME")
then {name: {surname: .SURNAME, code: "some code"} }
else empty end;
map([pick_nationality, pick_surname])

Related

jq error cannot iterate over null but need to make a different choice

I have a jq filter that selects the rows I need. But sometimes these lines can be empty, and then everything breaks and the rule does not work. I tried to use the if-then-else construct but to no avail.
A rule that works if you process the following json:
.metadata.namespace as $ns | (.spec.rules[0].match.any[].resources.kinds[] / "/") | [select(.[1])[0] // null, select(.[2])[1] // null, last] as [$version,$group,$kind] | {namespace: $ns, kind: $kind, group: $version, version: $group} | with_entries(select(.value!=null))
suitable json:
{
"apiVersion": "kyverno.io/v1",
"kind": "posdfsdf",
"metadata": {
"name": "e-eion",
"namespace": "kke",
"annotations": {
"policies.kyverno.io/title": "Dation",
"policies.kyverno.io/category": "Pod Security Standards (Restricted)",
"policies.kyverno.io/severity": "medium",
"policies.kyverno.io/subject": "Pod",
"kyverno.io/kyverno-version": "1.6.0",
"kyverno.io/kubernetes-version": "1.22-1.23",
"policies.kyverno.io/description": "se`. "
}
},
"spec": {
"validationFailureAction": "audit",
"background": true,
"rules": [
{
"name": "tion",
"match": {
"any": [
{
"resources": {
"kinds": [
"Pod"
]
}
}
]
},
"validate": {
"message": "Prisd",
"pattern": {
"spec": {
"=(eners)": [
{
"secxt": {
"altion": "false"
}
}
],
"=(i)": [
{
"sext": {
"alcalation": "false"
}
}
],
"containers": [
{
"setext": {
"an": "false"
}
}
]
}
}
}
}
]
}
}
example on which the rule stops working:
{
"apiVersion": "k/v1",
"kind": "Picy",
"metadata": {
"name": "denylation",
"namespace": "what",
},
"spec": {
"validationFailureAction": "audit",
"background": true,
"rules": [
{
"name": "deny-privilege-escalation",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"message": "Priviles[*].securityContext.allowPrind spec.initContalse`.",
"pattern": {
"spec": {
"=(iners)": [
{
"=(seext)": {
"=(aln)": "false"
}
}
],
"containers": [
{
"=(stext)": {
"=(al)": "false"
}
}
]
}
}
}
}
]
}
}
how can this be fixed? I need the rule to work out in any cases and give output
This should work :
.metadata.namespace as $ns |
((.spec.rules[0].match | .. | (objects | .resources.kinds[]?)) / "/") |
[select(.[1])[0] // null, select(.[2])[1] // null, last] as [$version,$group,$kind] |
{namespace: $ns, kind: $kind, group: $version, version: $group} |
with_entries(select(.value!=null))
The failing json gives the following error:
parse error: Expected another key-value pair at line 7, column 3
This is caused by the trailing comma found here:
"metadata": {
"name": "denylation",
"namespace": "what",
},
Removing that comma, the following error is thrown:
jq: error (at :44): Cannot iterate over null (null)
This is caused by the missing any key inside the match object.
We can 'catch' that error using a ? (docs):
(.spec.rules[0].match.any[]?.resources.kinds[] / "/")
^
But due to the missing key, the filter does not find anything and the output is empty.
Updated jqPlay

Combine multiple json to single json using jq

I am new to jq and stuck with this problem for a while. Any help is appreciable.
I have two json files,
In file1.json:
{
"version": 4,
"group1": [
{
"name":"olditem1",
"content": "old content"
}
],
"group2": [
{
"name":"olditem2"
}
]
}
And in file2.json:
{
"group1": [
{
"name" : "newitem1"
},
{
"name":"olditem1",
"content": "new content"
}
],
"group2": [
{
"name" : "newitem2"
}
]
}
Expected result is:
{
"version": 4,
"group1": [
{
"name":"olditem1",
"content": "old content"
},
{
"name" : "newitem1"
}
],
"group2": [
{
"name":"olditem2"
},
{
"name" : "newitem2"
}
]
}
Criterial for merge:
Has to merge only group1 and group2
Match only by name
I have tried
jq -S '.group1+=.group1|.group1|unique_by(.name)' file1.json file2.json
but this is filtering group1 and all other info are lost.
This approach uses INDEX to create a dictionary of unique elements based on their .name field, reduce to iterate over the group fields to be considered, and an initial state created by combining the slurped (-s) input files using add after removing the group fileds to be processed separately using del.
jq -s '
[ "group1", "group2" ] as $gs | . as $in | reduce $gs[] as $g (
map(del(.[$gs[]])) | add; .[$g] = [INDEX($in[][$g][]; .name)[]]
)
' file1.json file2.json
{
"version": 4,
"group1": [
{
"name": "olditem1",
"content": "new content"
},
{
"name": "newitem1"
}
],
"group2": [
{
"name": "olditem2"
},
{
"name": "newitem2"
}
]
}
Demo

jq: avoid empty arrays mapped field

Here my jq script:
def pick_nationality:
select(.NACIONALITAT) |
{nation: {country: .NACIONALITAT, code: "some code"} };
def pick_surname:
select(.SURNAME) |
{name: {surname: .SURNAME, code: "some code"} };
def pick_extension:
{ use: "official", extension: [pick_nationality, pick_surname] };
map(pick_extension)
Input json is like:
{
"SURNAME": "surname1"
}
{
"NACIONALITAT": "nacionalitat1"
}
However, sometimes any input objects don't contain any look up field:
{
"field1": "value1"
}
{
"field2": "value2"
}
Above script returns:
[
{
"use": "official",
"extension": []
},
{
"use": "official",
"extension": []
}
]
I'd like extension doesn't appear:
[
{
"use": "official"
},
{
"use": "official"
}
]
Any ideas?
You can simply add
| del(..|select(. == []))
as a trailing to your script in order to remove all such empty arrays
Demo
extend your function pick_extension for the desired output:
def pick_extension:
[pick_nationality, pick_surname] as $extension
| { use: "official" }
| if $extension | length > 0 then . + {extension: $extension} else . end;
If no extension could be picked, the empty array will no longer be added to the json object this way.

How can I post nested data to the firebase firestore with HTTP requests?

So I've been playing around with the firebase firestore feature for my REST API backend for a Godot project I'm working on. I'm using HTTP requests to post and get data from the database. So far I've only been using simple types of data, associating one user's ID to his or her username as a string.
In my code, I've defined a function that is responsible for doing the actual saving of a new file through an HTTP request:
func save_document(path: String, fields: Dictionary, http: HTTPRequest) -> void:
var document = { "fields": fields }
var body = to_json(document)
var url = FIRESTORE_URL + path
var res = http.request(url, _get_request_headers(), false, HTTPClient.METHOD_POST, body)
if res == OK:
print("successfully created document")
else:
print("failed creating document")
Then I call the function:
var payload_body_1 = {
"name": {
"stringValue": username.text
}
}
save_document("users?documentId=%s" % firebase.user_info.id, payload_body_1, http)
All of the above works 100% fine. But when I try to post more complicated data, such as a dictionary of dictionaries of dictionaries, I run into some problems.
When I run the following code:
var res = {
"north": {"spades": [], "hearts": [], "diamonds": [], "clubs": []},
"south": {"spades": [], "hearts": [], "diamonds": [], "clubs": []},
"east": {"spades": [], "hearts": [], "diamonds": [], "clubs": []},
"west": {"spades": [], "hearts": [], "diamonds": [], "clubs": []},
}
save_document("online?documentId=test", res, http)
It doesn't work. I imagine it has something to do with the "stringValue" key I used in the case where it worked, but I can't seem to figure out what to do when the value isn't a string value. Does anyone know how to solve this?
Ok so I actually solved it but I thought I might as well post the answer here if someone else potentially has the same problem in the future.
So basically, every field needs a data type, for instance, "stringValue". for Dictionaries, you need "mapValue", and for lists, you need "arrayValue". In the above Example, you would need to use some code looking like this:
var res = {
"north": { "mapValue": { "fields": {
"spades": { "arrayValue": { "values": [] } },
"hearts": { "arrayValue": { "values": [] } },
"diamonds": { "arrayValue": { "values": [] } },
"clubs": { "arrayValue": { "values": [] } }
} } },
"south": { "mapValue": { "fields": {
"spades": { "arrayValue": { "values": [] } },
"hearts": { "arrayValue": { "values": [] } },
"diamonds": { "arrayValue": { "values": [] } },
"clubs": { "arrayValue": { "values": [] } }
} } },
"east": { "mapValue": { "fields": {
"spades": { "arrayValue": { "values": [] } },
"hearts": { "arrayValue": { "values": [] } },
"diamonds": { "arrayValue": { "values": [] } },
"clubs": { "arrayValue": { "values": [] } }
} } },
"west": { "mapValue": { "fields": {
"spades": { "arrayValue": { "values": [] } },
"hearts": { "arrayValue": { "values": [] } },
"diamonds": { "arrayValue": { "values": [] } },
"clubs": { "arrayValue": { "values": [] } }
} } }
}
the mapValues need the "fields" key which is a dictionary of the keys in the dictionary associated with "mapValue". The arrayValues need something similar, but "values" instead of "fields". each element of the array in values should be a dictionary, and if you want the array for example to contain strings, each element should look like:
{ "stringValue": "text" }

JSONpath, only return result if other element has a value

I am looking for the JSONpath expression to extract elements in an array only if it contains another element.
The following expression returns all elements;
$.differ.element[*]['id','alias']
This is the JSON file:
{
"differ": {
"element": [
{
"id": "Address",
"alias": [
"Information about address"
]
},
{
"id": "Address.extension",
"path": "Address.extension"
},
{
"id": "Address.extension:official",
"path": "Address.extension",
"alias": [
"Mark address"
]
}
]
}
}
This results in the following output:
[
"Address",
[
"Information about address"
],
"Address.extension",
"Address.extension:official",
[
"Mark address"
]
]
I would like to omit the 'Address.extension' element as it does not have an alias.
How can I achieve this?
You can try with this
$.differ.element[?(#.alias)]
will get output like this :
[
{
"id":"Address",
"alias":[
"Information about address"
]
},
{
"id":"Address.extension:official",
"path":"Address.extension",
"alias":[
"Mark address"
]
}
]

Resources