JMESPath JSON filter with multiple matches - json

I have a json block that looks a bit like this (have you guessed from AWS)
{ "Vpcs":[
{
"VpcId":"vpc-blabla1",
"OtherKey":"Value"
},
{
"VpcId":"vpc-blabla2",
"OtherKey":"Value"
},
{
"VpcId":"vpc-blabla3",
"OtherKey":"Value"
},
{
"VpcId":"vpc-blabla4",
"OtherKey":"Value"
}]
}
I want to use JMESPath to get the OtherKey value for vpc-blabla1 and vpc-blabla3 (Examples, could be any list of vpc-id)
I can get blabla1 with JMESpath filter
Vpcs[?VpcId=='blabla1'].OtherKey
But I can't find the syntax for multiple values? I have tried the Or syntax || and the composite syntax | but neither works? - See below for things I have tried.
Vpcs[?VpcId=='blabla1' || 'blabla1'].OtherKey
Vpcs[?VpcId=='blabla1' || ?VpcId=='blabla1'].OtherKey
Vpcs[(?VpcId=='blabla1') || (?VpcId=='blabla1')].OtherKey
Vpcs[?VpcId=='blabla1' | ?VpcId=='blabla1'].OtherKey
Any suggestions? Is this possible or am I going to have to gather one result set at a time and recombine the results I want?

The general syntax for multiple is [? expr1 || expr2] so in your case, you can use:
Vpcs[?VpcId=='vpc-blabla1' || VpcId=='vpc-blabla2'].OtherKey
Another option, if you have many VPC ids you're search for, you can also say:
Vpcs[?contains(`["vpc-blabla1", "vpc-blabla2"]`, VpcId)].OtherKey

Related

How to select an element in an array based on two conditions in JMESPath?

I'm trying to select the SerialNumber of a specific AWS MFADevice for different profiles.
This command returns the list of MFADevices for a certain profile:
aws iam list-mfa-devices --profile xxx
and this is a sample JSON output:
{
"MFADevices": [
{
"UserName": "foobar#example.com",
"SerialNumber": "arn:aws:iam::000000000000:mfa/foo",
"EnableDate": "2022-12-06T16:23:41+00:00"
},
{
"UserName": "barfoo#example.com",
"SerialNumber": "arn:aws:iam::111111111111:mfa/bar_cli",
"EnableDate": "2022-12-12T09:13:10+00:00"
}
]
}
I would like to select the SerialNumber of the device containing the string cli. But in case there is only one device in the list (regardless of the presence or absence of the string cli), I'd like to get its SerialNumber.
I have this expression which already filters for the first condition, namely the desired string:
aws iam list-mfa-devices --profile xxx --query 'MFADevices[].SerialNumber | [?contains(#,`cli`)] | [0]'
However I still haven't been able to figure out how to add the if number_of_devices == 1 then return the serial of that single device.
I can get the number of MFADevices with this command:
aws iam list-mfa-devices --profile yyy --query 'length(MFADevices)'
And as a first step towards my final solution I wanted to initially get the SerialNumber only in the case the list has exactly one element, so, I thought of something like this:
aws iam list-mfa-devices --profile yyy --query 'MFADevices[].SerialNumber | [?length(MFADevices) ==`1`]'
but actually already at this stage I get the error below (left alone the fact that I still need to combine it with the cli part):
In function length(), invalid type for value: None, expected one of: ['string', 'array', 'object'], received: "null"
Does anybody know how to achieve what I want?
I know that I could just pipe the raw output to jq and do the filtering there, but I was wondering if there is a way to do it directly in the command using some JMESPath expression.
In order to do those kind of condition in JMESPath you will have to rely on logical or (||) and logical and (&&), because the language does not have a conditional keyword, per se.
So, in pseudo-code, instead of doing:
if length(MFADevices) == 1
MFADevices[0]
else
MFADevices[?someFilter]
You have to do, like in bash:
length(MFADevices) == 1 and MFADevices[0] or MFADevices[?someFilter]
So, in JMESPath:
length(MFADevices) == `1`
&& MFADevices[0].SerialNumber
|| (MFADevices[?contains(SerialNumber, `cli`)] | [0]).SerialNumber
Note: this assumes that, if there are more than one element but none contains cli, we should get null.
If you want the first element, even when there are multiple devices and the SerialNumber does not contains cli, then you can simplify it further and simply do a logical or, when the contains filter return nothing (as a null result will evaluates to false):
(MFADevices[?contains(SerialNumber, `cli`)] | [0]).SerialNumber
|| MFADevices[0].SerialNumber
With stedolan/jq you can filter for the substring and unconditonally add the first, then take the first of them:
.MFADevices | map(.SerialNumber) | first((.[] | select(contains("cli"))), first)
Demo
or
[.MFADevices[].SerialNumber] | map(select(contains("cli"))) + .[:1] | first
Demo
Output:
arn:aws:iam::111111111111:mfa/bar_cli

JSONPath issu returning value using a filter

I have the following json, for which I'm having trouble selecting one of the values using a filter. Specifically, I want to return "POS_Undeclared"
{"wd:Program_of_Study_Reference": {
"wd:ID": [
{
"#wd:type": "WID",
"$": "123456789abcde"
},
{
"#wd:type": "Program_of_Study_ID",
"$": "POS_Undeclared"
}
]
}
}
This jsonpath $.wd:Program_of_Study_Reference.wd:ID[1].$ gives me what I need, but I cannot count on the ordering to be consistent or even exist, hence cannot use [1] .
I cannot seem to get a filter like #wd:type == Program_of_Study_ID to work. I'm guessing it is the # and/or the : goofing up my syntax.
How can I filter to get the value POS_Undeclared ?
Try using
$.wd:Program_of_Study_Reference.*[?(#['#wd:type'] == "Program_of_Study_ID")].$

Jmeter - extract json data from dynamic payload using 'and' statement

I want to extract value from dynamic JSON.
This generation is different every time when is executed.
I need to get ex: XLIfccMNLv1asVam3QuatowCmrp8IYuE0FUDMYncegs=
which is generated in the different location in the Json file, with different value
I tried with.
$.payload[?(#.eventType == 'AAA')].entityId
which is working fine.
But, i want more stronger query.
Is it possible to use && statement with the query something like:
$.payload[?(#.eventType == 'AAA')&&(#.outgoingCurrency== 'EUR')].entityId
My payload:
{
"payload":[
{
"entityId":"qvr_IlDhTdzldeccxguNR84sE0N78DUfNGzwH-3pY7Y=",
"accountHolderId":"dvwxpTxVHdo2n1d5ytO6WyhnI2nuaEuzsh47agPpSFU=",
"processorType":"DUMMY",
"eventType":"AAA",
"outgoingCurrency":"USD",
"holdPeriodInHours":11,
"disabled":false
},
{
"entityId":"XLIfccMNLv1asVam3QuatowCmrp8IYuE0FUDMYncegs=",
"accountHolderId":"Xoo8uAM90qRT7kceDUJBIIqafUuUdH2fH_Ia2z1TY5w=",
"processorType":"DUMMY",
"eventType":"BBB",
"outgoingCurrency":"EUR",
"holdPeriodInHours":10,
"disabled":false
},
{
"entityId":"yBoHvYkyszaQpaFe1zvqCY416_vYiq7iivA9bWJhiTg=",
"processorType":"BMO_CPR",
"eventType":"AAA",
"disabled":false
}
]
}
You need to use the operation && inside the expression:
$.payload[?(#.outgoingCurrency== 'EUR' && #.eventType == 'AAA')].entityId.
For more details see: https://goessner.net/articles/JsonPath/

Jmeter Json Extractor with multiple conditional - failed

I am trying to create a Json Extractor and it`s being a thought activity. I have this json structure:
[
{
"reportType":{
"id":3,
"nomeTipoRelatorio":"etc etc etc",
"descricaoTipoRelatorio":"etc etc etc",
"esExibeSite":"S",
"esExibeEmail":"S",
"esExibeFisico":"N"
},
"account":{
"id":9999999,
"holdersName":"etc etc etc",
"accountNamber":"9999999",
"nickname":null
},
"file":{
"id":2913847,
"typeId":null,
"version":null,
"name":null,
"format":null,
"description":"description",
"typeCode":null,
"size":153196,
"mimeType":null,
"file":null,
"publicationDate":"2018-12-05",
"referenceStartDate":"2018-12-05",
"referenceEndDate":"2018-12-06",
"extension":null,
"fileStatusLog":{
"idArquivo":2913847,
"dhAlteracao":"2018-12-05",
"nmSistema":"SISTEMA X",
"idUsuario":999999,
"reportStatusIndicador":"Z"
}
}
}
]
What I need to do: First of all, I am using the option "Compute concatenation var" and "Match No." as -1. Because the service can bring in the response many of those.
I have to verify, if "reportStatusIndicador" = 'Z' or 'Y', if positive, I have to collect File.Id OR file.FileStatusLog.idArquivo, they are the same, I was trying the first option, in this case the number "2913847", but if come more results, I will collect all File.id`s
With this values in hands, I will continue with a for each for all File.id`s.
My last try, was this combination, after reading a lot and tried many others combinations.
[?(#...file.fileStatusLog.reportStatusIndicador == 'Z' || #...file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
But my debug post processor always appears like this, empty:
filesIds=
Go for $..[?(#.file.fileStatusLog.reportStatusIndicador == 'Z' || #.file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
Demo:
References:
Jayway JsonPath: Inline Predicates
JMeter's JSON Path Extractor Plugin - Advanced Usage Scenarios
I could do it with this pattern:
[?(#.file.fileStatusLog.reportStatusIndicador == 'Z' ||
#.file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
filesIds_ALL=2913755,2913756,2913758,2913759,2913760,2913761,2913762,2913763,2913764,2913765,2913766,2913767,2913768,2913769,2913770

How do I search for a specific string in a JSON Postgres data type column?

I have a column named params in a table named reports which contains JSON.
I need to find which rows contain the text 'authVar' anywhere in the JSON array. I don't know the path or level in which the text could appear.
I want to just search through the JSON with a standard like operator.
Something like:
SELECT * FROM reports
WHERE params LIKE '%authVar%'
I have searched and googled and read the Postgres docs. I don't understand the JSON data type very well, and figure I am missing something easy.
The JSON looks something like this.
[
{
"tileId":18811,
"Params":{
"data":[
{
"name":"Week Ending",
"color":"#27B5E1",
"report":"report1",
"locations":{
"c1":0,
"c2":0,
"r1":"authVar",
"r2":66
}
}
]
}
}
]
In Postgres 11 or earlier it is possible to recursively walk through an unknown json structure, but it would be rather complex and costly. I would propose the brute force method which should work well:
select *
from reports
where params::text like '%authVar%';
-- or
-- where params::text like '%"authVar"%';
-- if you are looking for the exact value
The query is very fast but may return unexpected extra rows in cases when the searched string is a part of one of the keys.
In Postgres 12+ the recursive searching in JSONB is pretty comfortable with the new feature of jsonpath.
Find a string value containing authVar:
select *
from reports
where jsonb_path_exists(params, '$.** ? (#.type() == "string" && # like_regex "authVar")')
The jsonpath:
$.** find any value at any level (recursive processing)
? where
#.type() == "string" value is string
&& and
# like_regex "authVar" value contains 'authVar'
Or find the exact value:
select *
from reports
where jsonb_path_exists(params, '$.** ? (# == "authVar")')
Read in the documentation:
The SQL/JSON Path Language
jsonpath Type