Cannot index array with string "xxx" - jq

I have a json file as this,
# cat input.json
{
"foo":[
"key1",
"key2"
],
"bar": "key3"
}
And I want to join all those strings ("key1", "key2", key3") into one string, and I define the filter as this,
# cat filter.jq
[.[] | . as { foo: $names, bar: $name} | {
name1: [ $names | range(0;length) as $i | {
key1: ($names[$i])
}],
name2: {
key2: $name
}
} | {
values: (.name1 | map(.key1) | join(" ") + .name2.key2)
}]
But this doesn't work,
# <input.json jq --slurp --from-file filter.jq > output.json
jq: error (at <stdin>:8): Cannot index array with string "name2"
What's wrong going here?
btw. concat with a string seems works.
values: (.name1 | map(.key1) | join(" ") + "key3")

The problem is underspecified, but you might wish to consider:
[.. | strings] | join(" ")
or similar.

Related

Using jq I want to modify multiple values

I have a json like this:
{
"bla": { "body": {
"mode": "raw",
"raw": "{\n \"accountId\": \"1111\",\n \"monetaryAmount\": {\n \"amount\": 111,\n \"exponent\": 2,\n \"currency\": \"aaa\"\n },\n \"remarks\": \"consequat quis\"\n}",
"options": {
"raw": {
"language": "json"
}
}
}},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{token}}",
"type": "string"
}
]
},
"segg": {
"key": "txn_id",
"value": "{{token}}",
"type": "string"
},
"slugg": {
"key": "companyId",
"value": "{{token}}",
"type": "string"
},
"blu": [ {
"key": "teamMemberId",
"value": "{{token}}",
"type": "string"
} ]
}
Conditions: There can be anywhere any deep ..body.raw strings, on these I want to do a simple search and replace (Or better: Parse the string as JSon and do some jq on that?).
And any deep objects with .key and .value (strings), in this I just want to replace the .value="{{"+.key+"}}"
Thanks
This is what I could do:
del(.. | objects | .auth)|
(.. | objects | select(.key == "teamMemberId")).value="{{teamMemberId}}"|
(.. | objects | select(.key == "accountId")).value="{{accountId}}"|
(.. | objects | select(.key == "companyId")).value="{{companyId}}"|
(.. | objects | select(.key == "transactionId")).value="{{transactionId}}"|
(.. | objects | select(.key == "txn_id")).value="{{transactionId}}"|
(.. | objects | select(.key == "limitType")).value="MONTHLY"|
(.. | objects | select(.raw | type == "string")).raw=null
In case of ..raw I can not find a way to replace, and in case of .key -> .value I would prefer something like find by regex, and replace as mentioned. So that the whole jq expression is more compact.
One way to process the .body.raw values would be to start with:
(.. | try .body.raw // empty) |= fromjson
You can elaborate fromjson as per your requirements.
One way to update the .value values would be to include the following in your jq pipeline:
walk(if type == "object" and has("key") and has("value")
then .value = "{{\(.key)}}" else . end)

jq: pick value if field exist

This is my script:
def pick_partOf($uids):
(reduce $uids[] as $uid (
{};
. + ($uid | {(.oid1): .id})
)) as $dict
| map(. + { partOf: $dict[."parent-identifier-value"] } | del(..|nulls));
def pick_organization:
{
resourceType: "Organization",
active: true,
partOf: .partOf?
};
pick_partOf($identifiers) | pick_organization
Problem here, is that sometimes some input objects doesn't contains ."parent-identifier-value", and I'm getting:
jq: error (at rsan.json:7): Cannot index object with null
Those are my input objects:
{
"identifier-value": "61",
"name": "name61"
}
{
"id": "62",
"name": "name62",
"parent-identifier-value": "61"
}
Currently, I'm launching jq using empty array as $identifiers arg:
jq -s -f build-organization-bundle.jq --argjson identifiers '[]' rsan.json
However, my identifiers is an other json files like this:
{
"id": "be8a02b6-54b9-450f-bf45-f28ab7ebf2dd",
"oid1": "61"
}
{
"id": "f1652f3a-bca9-433c-a2c4-c924811196f6",
"oid1": "62"
}
I've tried with:
def pick_partOf($uids):
(reduce $uids[] as $uid (
{};
. + ($uid | {(.oid1): .id})
)) as $dict
| map(. + (
if (.|has("parent-identifier-value")) then
{ partOf: $dict[."parent-identifier-value"] }
else null end
))
| del(..|nulls));
But here I'm facing with some syntax issue I don't quite figure out how to fix it.
You did not specify why you want pick_partOf to achieve.
If its output should be
{
"identifier-value": "61",
"name": "name61"
}
{
"id": "62",
"name": "name62",
"parent-identifier-value": "61",
"partOf": "be8a02b6-54b9-450f-bf45-f28ab7ebf2dd"
}
then you can use
if ."parent-identifier-value" then
.partOf = $dict[."parent-identifier-value"]
else
.
end
Demo on jqplay
Repeating ."parent-identifier-value" was annoying me.
."parent-identifier-value" as $piv |
if $piv then .partOf = $dict[$piv] else . end
Demo on jqplay

jq: where select clause is applied

Here my documents:
[
{
"id":"f3b8c257-9950-45e7-9e79-ace19ec8905e",
"identifier":[
{
"system":{
"value":"urn:oid:2.16.724.4.9.10.2"
},
"value":{
"value":"10839812"
}
}
]
},
{
"id":"f0a1e3ae-826f-4a03-b29e-10ef3bc86ea0",
"identifier":null
}
]
Currently, I'm aplying this jq filter:
map(
{
id,
dni: .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
However, I'm getting this message:
jq: error (at practitioner-mongoexport.json:146715): Cannot iterate over null (null)
As you can guess, problem arises processing second object where .identifier: null.
I've tried with that:
map(
{
id,
dni: select(.identifier) | .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
I've also tried with:
map(
select(.identifier) |
{
id,
dni: .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
Then result is [].
I don't quite figure out what I'm doing wrong.
My desired output would be:
[
{
"id":"f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]
Any ideas?
You need to deal with the case if .identifier is null. Here, the ? operator may help.
without selection
jq 'map({id, dni: (.identifier[]?).value.value})'
Demo
with selection
jq 'map({id, dni: (.identifier[]? | select(.system.value == "urn:oid:2.16.724.4.9.10.2")).value.value})'
Demo
[
{
"id": "f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]
map(
select(.identifier?[]?.system.value == "urn:oid:2.16.724.4.9.10.2")
| { id, "dni": .identifier[].value.value }
)
Will generate:
[
{
"id": "f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]
The trick here is .identifier?[]?.system.value where the ? will ignore .identifier if it's null
JqPlay Demo

Accessing field with jq that can be string or array

I have a large dump of data in json that looks like:
[{
"recordList" : {
"record" : [{
"Production" : {
"creator" : {
"name" : "A"
}
}
},
{
"Production" : {}
},
{
"Production" : [{
"creator" : {
"name" : "B"
},
"creator" : {
"name" : "C"
}
}]
}]
}
}]
I need to check if there is at least one creator in a record or not. If there is I give a 1 else a 0 for that field in a CSV-file.
My code:
jq -r '.[].recordList.record[]|"\(if ((.Production.creator.name)? // (.Production[]?.creator.name)?) == null or ((.Production.creator.name)?|length // (.Production[]?.creator.name)?|length) == 0 then 0 else 1 end),"' file.json
The problem is that the field 'Production' is only an array when there are multiple creators.
The result I want to get in this case is:
1,
0,
1,
jq solution:
jq -r '.[].recordList.record[].Production
| "\(if ((type == "array" and .[0].creator.name !="")
or (type == "object" and .creator.name and .creator.name !=""))
then 1 else 0 end),"' file.json
The output:
1,
0,
1,
Simplified jq solution:
jq -r '.[].recordList.record[].Production
| ((type == "array" and .[0].creator.name) or .creator.name)
| if . then "1," else "0," end' file.json

Interaction between {} and select

Here's my test data:
[
{
"id": "id-1",
"tags": {
"key": "name",
"value": "name-1"
}
},
{
"id": "id-2"
}
]
I'm trying to simplify the output, to show the 'name' field if present, and always show the id. For example, this script almost works:
~ $ cat testdata | jq '.[] | {id, name: .tags.value}'
{
"id": "id-1",
"name": "name-1"
}
{
"id": "id-2",
"name": null
}
When I try to add in a guard against .keys not existing and filter for the section of 'keys' I care about, here's what happens:
~ $ cat testdata | jq '.[] | {id, name: (select(.tags.key == "name") | .tags.value)}'
{
"id": "id-1",
"name": "name-1"
}
I assume {} is somehow ending up with a zero-length array instead of 'null'. What should I be using instead of |? What am I misunderstanding?
I ended up solving the problem using: [POSSIBLY_MATCHED_EXPRESSION][0], in this case:
cat testdata | jq '.[] | {id, name: ([select(.tags.key == "name") | .tags.value][0])}'
If I'm understanding correctly, if you wanted to include a name only if it existed, I'd do this:
map({id} + with_entries(select(.key == "tags") | .value))
Otherwise if you don't mind null names:
map({id, name: with_entries(select(.key == "tags") | .value) | .name})
Here's a more general solution if you have other "tags" so it's not hardcoded to only accept name values.
This assumes that any object value is actually a key/value pair.
map(with_entries(if .value | type == "object" then .value else . end))
Or if tags is the only dynamic property:
map(with_entries(if .key == "tags" then .value else . end))
If the goal is to produce:
{"id":"id-1","name":"name-1"}
{"id":"id-2"}
then the following three expressions are essentially equivalent solutions:
.[] | if .tags.key == "name" then {id, name: .tags.value} else {id} end
.[] | {id} + (if .tags.key == "name" then {name: .tags.value} else {} end)
.[] | (select(.tags.key == "name") | {id, name: .tags.value}) // {id}
You could just add
| if .name == null then del(.name) else . end
to the end of your filter to get rid of the .name key when its value is null.
With your test data, the following
.[]
| {id, name:.tags.value }
| if .name == null then del(.name) else . end
produces
{
"id": "id-1",
"name": "name-1"
}
{
"id": "id-2"
}

Resources