Extract nested json map - groovy

Have JSON received from some REST API:
{
"advertiser_id": {
"8253":{
"name":"Signify",
"id":8253
},
"2920":{
"name":"Hyundai",
"id":2920
}
}
}
I wan't to extract maps inside numbers like 8253, 2920 but without hard mapping these numbers, they can be different at any time. Anyway these numbers just duplicates id inside.
Expected output after transformation:
[
{
"name":"Signify",
"id":8253
},
{
"name":"Hyundai",
"id":2920
}
]
Tried with:
import groovy.json.*
def json = '''
'''
def p = new JsonSlurper().parseText(json)
def result = p["advertiser_id"].collectEntries{ k, v ->
[
id: v.id,
name: v.name
]
}
But it returns only one "object":
{
"id": 8905,
"name": "Spotify"
}
Also achieved wrong result with next code:
def a = p["advertiser_id"].collectMany {
it.value.collect{ k, v ->
[
id: k,
name: v
]
}
}

you want to build a list - so you need collect instead of collectEntries
def p = new JsonSlurper().parseText(json)
def result = p["advertiser_id"].collect{ k, v ->
[
id: v.id,
name: v.name
]
}
and you are not doing any transformation to nested objects - so, you could simplify the code to this:
def result = p.advertiser_id.collect{ k, v -> v }

Related

writeJSON groovy writes only first value to file

Below is my jenkins groovy script to read data and write as json file.
import groovy.json.JsonOutput
def arn = ""
def name = ""
def os = ""
pipeline {
agent any
stages {
stage('Hello') {
steps {
script{
def ret = sh(script: 'aws devicefarm list-devices', returnStdout: true)
def jsonObj = readJSON text: ret
def currentVersion = "13.0"
def values = currentVersion.split('\\.')
def json_str = ""
for(String item: jsonObj.devices) {
os = item.os
if(!os.contains("\\.")) {
osV = os + ".0"
} else{
osV = os
}
def osValues = osV.split('\\.')
if(values[0].toInteger() <= osValues[0].toInteger()) {
name = item.name
def data = [
name: "$name",
os: "$os",
]
json_str += JsonOutput.toJson(data)
}
}
def json_beauty = JsonOutput.prettyPrint(json_str)
writeJSON(file: 'message124.json', json: json_beauty)
}
}
}
}
}
But here, it only saves the first value. Not all the values. Could you tell me where I am wrong here
It's not 100% clear what you actually want to end up with, but I think you want a JSON file containing the items where OS is greater than a magic number.
It's helpful to provide enough data to duplicate the problem, and eliminate everything that isn't directly related. I think what you want is something like this:
jsonObj = [
devices: [
[ os: '3', name: 'Name 1'],
[ os: '10.2', name: 'Name 10.2'],
[ os: '7', name: 'Name 7'],
[ os: '3', name: 'Name 3'],
],
]
values = ['5']
def normalizeOs(os) {
os.contains(".") ? os : "$os.0"
}
def shouldSkip(normalizedOs) {
osValues = os.split('\\.')
values[0].toInteger() > osValues[0].toInteger()
}
selected = []
for (item: jsonObj.devices) {
os = normalizeOs(item.os)
if (shouldSkip(os)) continue
selected.push([name: item.name, os: os])
}
json = new groovy.json.JsonBuilder(selected)
println(json)
Outputs:
[{"name":"Name 7","os":"7.0"},{"name":"Name 10.2","os":"10.2"}]

Build Django Query Dynamically based on kendo UI filter

I'm trying to query a database based on user filter. i received following input from kendo UI grid.
{
"filter":{
"filters":[
{
"logic":"or",
"filters":[
{
"field":"aging",
"operator":"eq",
"value":24
},
{
"field":"aging",
"operator":"eq",
"value":13
}
]
},
{
"logic":"or",
"filters":[
{
"field":"follow_up_name",
"operator":"eq",
"value":"Call Insurance Provider"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"patient_name",
"operator":"eq",
"value":"kartik"
}
]
},
{
"logic":"and",
"filters":[
{
"field":"created_date",
"operator":"eq",
"value":"2022-01-09T18:30:00.000Z"
},
{
"field":"created_date",
"operator":"gte",
"value":"2022-01-04T18:30:00.000Z"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"follow_up_status",
"operator":"eq",
"value":"Open"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"role_name",
"operator":"eq",
"value":"Pharmacist"
}
]
},
{
"logic":"or",
"filters":[
{
"field":"last_response",
"operator":"eq",
"value":"To-Patient"
}
]
}
],
"logic":"and"
},
"skip":0,
"take":10
}
Based on above data i need both 'and' & 'or' condition to build query dynamically. and pass it to database. also filter can contain multiple list. also want to make these class common which can take only UI arguments build query and return. please provide a solution for these.
Q objects are your friend here.
from django.db.models import QuerySet, Q
from functools import reduce
def filter_rec(data: dict) -> Q:
logic = data.get('logic', None)
# Leaf node
if logic is None:
field = data['field']
operator = data['operator']
value = data['value']
return Q(**{f'{field}__{operator}': value})
# Node with children
children = list()
for item in data['filters']:
child = filter_rec(child)
children.append(child)
if logic == 'and':
query = reduce(lambda x, y: x & y, children)
elif logic == 'or':
query = reduce(lambda x, y: x | y, children)
return query
def kendo_filter(data: dict, qs: QuerySet) -> QuerySet:
query = filter_rec(data['filter'])
skip = data['skip']
take = data['take']
filtered_qs = qs.filter(query)
paginated_qs = filtered_qs[skip:skip+take]
return paginated_qs
The general gist is this. It is up to you to validate the data and do error handling.
filter_rec(data['filter']) returns the following for your example data:
<Q: (
AND:
(OR: ('aging__eq', 24), ('aging__eq', 13)),
('follow_up_name__eq', 'Call Insurance Provider'),
('patient_name__eq', 'kartik'),
('created_date__eq', '2022-01-09T18:30:00.000Z'),
('created_date__gte', '2022-01-04T18:30:00.000Z'),
('follow_up_status__eq', 'Open'),
('role_name__eq', 'Pharmacist'),
('last_response__eq', 'To-Patient')
)>
Since single ORs would be lifted as is and nested ANDs would show up as a single level, it seems to have worked.

JSON Extract to dataframe using python

I have a JSON file and the structure of the file is as below
[json file with the structure][1]
I am trying to get all the details into dataframe or tabular form, Tried using denormalize and could not get the actual result.
{
"body": [{
"_id": {
"s": 0,
"i": "5ea6c8ee24826b48cc560e1c"
},
"fdfdsfdsf": "V2_1_0",
"dsd": "INDIA-",
"sdsd": "df-as-3e-ds",
"dsd": 123,
"dsds": [{
"dsd": "s_10",
"dsds": [{
"dsdsd": "OFFICIAL",
"dssd": {
"dsds": {
"sdsd": "IND",
"dsads": 0.0
}
},
"sadsad": [{
"fdsd": "ABC",
"dds": {
"dsd": "INR",
"dfdsfd": -1825.717444
},
"dsss": [{
"id": "A:B",
"dsdsd": "A.B"
}
]
}, {
"name": "dssadsa",
"sadds": {
"sdsads": "INR",
"dsadsad": 180.831415
},
"xcs": "L:M",
"sds": "L.M"
}
]
}
]
}
]
}
]
}
This structure is far too nested to put directly into a dataframe. First, you'll need to use the ol' flatten_json function. This function isn't in a library (to my knowledge), but you see it around a lot. Save it somewhere.
def flatten_json(nested_json):
"""
Flatten json object with nested keys into a single level.
Args:
nested_json: A nested json object.
Returns:
The flattened json object if successful, None otherwise.
"""
out = {}
def flatten(x, name=''):
if type(x) is dict:
for a in x:
flatten(x[a], name + a + '_')
elif type(x) is list:
i = 0
for a in x:
flatten(a, name + str(i) + '_')
i += 1
else:
out[name[:-1]] = x
flatten(nested_json)
return out
Applying it to your data:
import json
with open('deeply_nested.json', r) as f:
flattened_json = flatten_json(json.load(f))
df = pd.json_normalize(flattened_json)
df.columns
Index(['body_0__id_s', 'body_0__id_i', 'body_0_schemaVersion',
'body_0_snapUUID', 'body_0_jobUUID', 'body_0_riskSourceID',
'body_0_scenarioSets_0_scenario',
'body_0_scenarioSets_0_modelSet_0_modelPolicyLabel',
'body_0_scenarioSets_0_modelSet_0_valuation_pv_unit',
'body_0_scenarioSets_0_modelSet_0_valuation_pv_value',
'body_0_scenarioSets_0_modelSet_0_measures_0_name',
'body_0_scenarioSets_0_modelSet_0_measures_0_value_unit',
'body_0_scenarioSets_0_modelSet_0_measures_0_value_value',
'body_0_scenarioSets_0_modelSet_0_measures_0_riskFactors_0_id',
'body_0_scenarioSets_0_modelSet_0_measures_0_riskFactors_0_underlyingRef',
'body_0_scenarioSets_0_modelSet_0_measures_1_name',
'body_0_scenarioSets_0_modelSet_0_measures_1_value_unit',
'body_0_scenarioSets_0_modelSet_0_measures_1_value_value',
'body_0_scenarioSets_0_modelSet_0_measures_1_riskFactors',
'body_0_scenarioSets_0_modelSet_0_measures_1_underlyingRef'],
dtype='object')

parsing boto3 describe_stacks response

Is there a more efficient way to parse the following response from cloudformation
I tried for loop but I am wondering if there is a better way
response = {
'Stacks': [
{
'Outputs': [
{
'Description': 'ARN of the load balancer',
'ExportName': 'xx',
'OutputKey': 'LoadBalancerARN',
'OutputValue': 'arn:aws:elasticloadbalancing:ss'
},
{
'Description': 'ARN of the ecs service',
'ExportName': 'xxx',
'OutputKey': 'ServiceARN',
'OutputValue': 'arn:aws:ecs:5O'
},
{
'Description': 'ARN of the ecs task definition',
'ExportName': 'xxx',
'OutputKey': 'TaskDefinitionARN',
'OutputValue': 'arn:aws:ecs:xxx9'
}
]
}
]
}
def main(response):
outputs = response['Stacks'][0]['Outputs']
for output in outputs:
for k, v in output.items():
if 'OutputKey' in k and 'ServiceARN' in v:
print('ServiceARN:{}'.format(output['OutputValue']))
if 'OutputKey' in k and 'TaskDefinitionARN' in v:
print('TaskDefinitionARN:
{}'.format(output['OutputValue']))
main(response)
I trying to get ServiceARN and TaskDefinitionARN values
I found another option
import json
response = {}
def main(response):
json_string = json.dumps(response)
x = json.loads(json_string)
for i in x['Stacks'][0]['Outputs']:
if i['OutputKey'] == "ServiceARN":
print(i['OutputValue'])
main(response)

how to get child key without going with parent key or with iteration

1.I am having a list which contains multiple Maps that looks like below Map.
Each map contains many keys I want to get value of "name"
{
"question":{
"com.forms.tree":{
"requiredByDefault":true,
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"language":"en"
},
"formField":{
"name":"CUSTOM_347",
"tag":"input",
"url":"Demo"
}
}
},
"Field":"true"
},{
"question":{
"com.forms.tree":{
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"language":"en"
},
"formField":{
"name":"CUSTOM_348",
"url":"Demo"
}
}
},
"Field":"true"}
I want to get the value of "name" which falls in every Map but don't want to iterate like question?."com.forms.tree"?.formField?.name.
Is there any other approach in groovy?
So given the json:
def jsonTxt = '''{
"question":{
"com.forms.tree":{
"requiredByDefault":true,
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"name": "test",
"language":"en"
},
"formField":{
"name":"CUSTOM_347",
"tag":"input",
"url":"Demo"
}
}
},
"Field":"true"
}'''
We can parse it with:
import groovy.json.*
def json = new JsonSlurper().parseText(jsonTxt)
You want to find the "formField" entry in that object, so lets write a recursive finder that will walk through out map of maps looking for the first entry with the given key:
static findFirstByKey(Map map, key) {
map.get(key) ?: map.findResult { k, v -> if(v in Map) findFirstByKey(v, key) }
}
And you can then check it works:
assert findFirstByKey(json, 'formField')?.name == "CUSTOM_347"

Resources