extract data from nested loop - python

I have a scenario where I am extracting data from a json response in this below conditions :
I am Looping through the json and finding value of active if active true then under the same parent array find under cls type if type is alpha1 return the eces value (in this case 260551).
if after looping through json there is no value of active as true or value of active is true but in that same parent array under cls type is not alpha1 then return not found.
Here I am getting the value of eces correctly but how can I get the value of the this fields as well address, c_m, active, type then construct a key value mapping of all the extracted data and save in a json file.
here is what I have tried :
found = False
for di in d:
if di.get('active', False):
for cl in di.get('cls', []):
if cl.get('type') == 'alpha1':
print(di['eces'])
found = True
if not found:
print("Not found")
desired json output :
{
"res1": [{
"eces": "260551",
"res2": [{
"c_m": 345,
"clsfrmt": [
{
"address": "{\"I_G\":\"CD\",\"I_D\":\"01\",\"I_Y\":\"C1\",\"I_XD\":\"04\",\"I_TY\":1,\"S_L\":\"https://testappsampler.com\",\"O_DC\":\"\"}",
"type": "Alpha"
}
],
"active": true
}]
}]
}
I am stuck in creating the json data in this structure , any help would be great.

While I'd advice to refactor this code in some proper way, this will create a mapping in very straight maner:
import json
dump = []
for di in d:
if di.get('active', False):
for cl in di.get('cls', []):
if cl.get('type') == 'alpha1':
dump.append(
{
"res1": [{
"eces": di['eces'],
"res2": [{
"c_m": di['c_m'],
"clsfrmt": [
{
"address": di['cls'][0]['address'],
"type": di['cls'][0]['type']
}
],
"active": di['active']
}]
}]
}
)
s = json.dumps(dump) # this is your JSON string
Result.

Related

Append to an array inside a JSON object based on key in python

I have some JSON I'm looping through in the following format. I need to create an object for each unique primary key found in the source data and append to an array. I'm not sure how I would create the object on first encounter of the key and append to it on the next encounter. My initial attempt just creates a new object for each object in the source. Wasn't able to find an example in python only js.
Source data format:
[
...
{
"Id": "NOT NEEDED DATA",
"Client": {
"Id": "KEY",
"Name": "NOT NEEDED DATA"
},
"Name": "DESIRED DATAPOINT"
},
...
]
Desired format:
[
...
{
"client_id": "KEY",
"locations": ["DATA", "DATA"]
}
...
]
pseudocode
for i in sourcedata:
client_id = i['Client']['Id']
location_name = i['Name']
obj = {
"client_id": client_id,
"locations": [location_name]
}
new_array.append(obj)
You can first iterate and build a dictionary for then creating a list of dictionaries as specified in your output format.
from collections import defaultdict
# create and populate the dictionary
d = defaultdict(list)
for i in sourcedata:
client_id = i['Client']['Id']
location_name = i['Name']
d[client_id].append(location_name)
# build the result
res = [{"client_id": k, "locations": v} for k,v in d.items()]

Safe get when parent is null in dictionary

I am looking for a way to safe get a value from a nested dictionary.
.get() will give None if the value is not present in a dictionary but if a value is None None.get("value_2") will throw an error.
Sample Dictionary:
[
{
"value": {
"value_2": "string"
}
},
{
"value": null
}
]
When iterating through the array for 0th element let us say a a.get("value").get("value_2") will give string as output, but for the second element a.get("value").get("value_2") gives an error. There needs to be a check if value is None, if not only then get value_2
Is there any way to skip the if check and make python return None. If the dictionary is nested for more than one level then I will have to check for None at multiple levels.
I would suggest to implement function like below
vals = [
{
"value": {
"value_2": "string"
}
},
{
"value": None
}
]
def get_from_dict(dict_, path):
path = path.split("/")[::-1]
dict_ = dict_.get(path.pop())
while dict_ is not None and len(path)>0:
dict_ = dict_.get(path.pop())
return dict_
for a in vals:
print(get_from_dict(a, "value/value_2"))

How to get length as 0 if the dictionary is not available in Json file while parsing using python

I am trying to get the length of a dictionary as below . for dictionary "ZZZZ" i may have multiple records available
for j in range(len(json_file['entitity'][i]['XXXX']['YYYYY']['ZZZZ']))
But if the dictionary doesn't exists in the json file i want to return them as 0
As per the above value i have requirement to get a variable value like below.
temp['EMPID'] = json_file['entities'][i]['XXXX']['YYYYY']['ZZZZ'][j]['re']['id']
Please help with an suggestion , how can i get "j" variable as 0 if the dictionary doesn't exist. Please find below example
"YYYYY": [
{
"ZZZZ": {
"id": "Z1234",
"type": "p1"
},
"id": "wer1234",
"prop": {
"dir": "South",
"Type": "C1"
}
},
{
"ZZZZ": {
"id": "Y1234",
"type": "p2"
},
"id": "ert12345",
"prop": {
"dir": "North",
"relationshipType": "C2"
}
}
]
In the above example , i am trying to get the value [ZZZZ][id] ( Value should be : "Z1234" ). In the same way i have one more record with
value "Y1234". I have totally 2 records because of that i am trying to capture the length as per below command and get the id value.
for j in range(len(json_file['YYYYY'])) ------###to capture the lenght as i have 2 records so i am trying to capture length 2
temp['EMPID'] = json_file['YYYYY'][j]['ZZZZ']['id'] -------##to capture the attribute value
But in some cases i may not receive these attributes in my source Json Files, where i want to handle if the attributes are available and have
multiple records then as per above statement i want to get the values else we can populate null values for these id columns.
You can accomplish this by using the dict.get(key, default) method, supplying an empty list as the default value if the key in the dictionary doesn't exist.
This will allow you to iterate over the keys in the dictionary at the specified key, if it exists, and skip it otherwise.
Ex:
data = {
'one': {},
'two': {
'a': {
're': {
'id': 1
}
},
'b': {
're': {
'id': 1
}
}
}
}
# Example with empty dictionary
for key in data.get('one', []):
print(f'data[\'one\'] - {key}: {data["one"][key]}')
# Example with populated dictionary
for key in data.get('two', []):
print(f'data[\'two\'] - {key}: {data["two"][key]}')
# Example with non-existent dictionary
for key in data.get('foo', []):
print(f'data[\'foo\'] - {key}: {data["foo"][key]}')

Comparing value in a JSON using Python

I receive a fairly uncomfortable JSON to work with, which looks as follows:
[
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}]
And I would like to compare a piece of data (more precisely - value of Button state) but I seem to fail. Tried following:
import requests
import json
yo = 1
switchPost = "http://192.168.0.104:7896/iot/d?k=123456789&i=san_1_switch&d=sw|{}"
robGet = "http://192.168.0.109:10100/robot/sen_2"
r = requests.get(robGet, headers={"content-type":"application/json"})
resp = json.loads(r.text)
for attrs in (resp['attributes']['value']):
if attrs['data'] == false:
yo = 100
break
g = requests.post(switchPost.format(yo), headers={"content-type":"text/plain"})
print(r.text)
Unfortunately, the error I receive is the following:
for attrs in (resp['attributes']['value']):
TypeError: list indices must be integers, not str
In your JSON, the fact that it is wrapped in [ then ] means it is a JSON array, but with just one element.
So, as your error message suggests, resp needs an integer as its index, for which element of the array you want. resp[0] then refers to
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}
(notice no [] now, so it's a JSON object)
Then you want resp[0]['attributes'] to refer to the single part of this object, 'attributes' which again refers to an array.
Therefore for attribute in resp[0]['attributes'] will allow you to loop through this array.
To get the boolean value you want, you'll then want to find which element of that array has 'name' of 'buttonState' and check the corresponding 'value'.
In all, you're probably looking for something like:
for attribute in resp[0]['attributes']:
if attribute['name'] == 'buttonState' and attribute['value']['data'] is False:
# Do your thing here
resp is a list so, to get first element, access it as resp[0]. Same with resp[0]['attributes']
So you can access it as follows
resp[0]['attributes'][0]['value']
You can restructure your for loop as follows
for d in resp[0]['attributes']:
if isinstance(d['value'], dict) and d['value'].get('data') == false:
yo = 100
break
The answer is in the error message I think:
TypeError: list indices must be integers, not str
The first entry in attributes has a value that is a list, so you can't get 'data' from that.
Since you have a mix of types, you might need to check if 'value' is a list or a dict.
Edit:
Jumped the gun here I think. #dennlinger gives an explanation to your error message. But you'll get it again once you're past that...

Python ---- TypeError: string indices must be integers

I have the below Python code
from flask import Flask, jsonify, json
app = Flask(__name__)
with open('C:/test.json', encoding="latin-1") as f:
dataset = json.loads(f.read())
#app.route('/api/PDL/<string:dataset_identifier>', methods=['GET'])
def get_task(dataset_identifier):
global dataset
dataset = [dataset for dataset in dataset if dataset['identifier'] == dataset_identifier]
if len(task) == 0:
abort(404)
return jsonify({'dataset': dataset})
if __name__ == '__main__':
app.run(debug=True)
Test.json looks like this:
{
"dataset": [{
"bureauCode": [
"016:00"
],
"description": "XYZ",
"contactPoint": {
"fn": "AG",
"hasEmail": "mailto:AG#AG.com"
},
"distribution": [
{
"format": "XLS",
"mediaType": "application/vnd.ms-excel",
"downloadURL": "https://www.example.com/xyz.xls"
}
],
"programCode": [
"000:000"
],
"keyword": [ "return to work",
],
"modified": "2015-10-14",
"title": "September 2015",
"publisher": {
"name": "abc"
},
"identifier": US-XYZ-ABC-36,
"rights": null,
"temporal": null,
"describedBy": null,
"accessLevel": "public",
"spatial": null,
"license": "http://creativecommons.org/publicdomain/zero/1.0/",
"references": [
"http://www.example.com/example.html"
]
}
],
"conformsTo": "https://example.com"
}
When I pass the variable in the URL like this: http://127.0.0.1:5000/api/PDL/1403
I get the following error: TypeError: string indices must be integers
Knowing that the "identifier" field is a string and I am passing the following in the URL:
http://127.0.0.1:5000/api/PDL/"US-XYZ-ABC-36"
http://127.0.0.1:5000/api/PDL/US-XYZ-ABC-36
I keep getting the following error:
TypeError: string indices must be integers
Any idea on what am I missing here? I am new to Python!
The problem is that you are trying to iterate the dictionary instead of the list of datasources inside it. As a consequence, you're iterating through the keys of the dictionary, which are strings. Additionaly, as it was mentioned by above, you will have problems if you use the same name for the list and the iterator variable.
This worked for me:
[ds for ds in dataset['dataset'] if ds['identifier'] == dataset_identifier]
The problem you have right now is that during iteration in the list comprehension, the very first iteration changes the name dataset from meaning the dict you json.loads-ed to a key of that dict (dicts iterate their keys). So when you try to look up a value in dataset with dataset['identifier'], dataset isn't the dict anymore, it's the str key of you're currently iterating.
Stop reusing the same name to mean different things.
From the JSON you posted, what you probably want is something like:
with open('C:/test.json', encoding="latin-1") as f:
alldata = json.loads(f.read())
#app.route('/api/PDL/<string:dataset_identifier>', methods=['GET'])
def get_task(dataset_identifier):
# Gets the list of data objects from top level object
# Could be inlined into list comprehension, replacing dataset with alldata['dataset']
dataset = alldata['dataset']
# data is a single object in that list, which should have an identifier key
# data_for_id is the list of objects passing the filter
data_for_id = [data for data in dataset if data['identifier'] == dataset_identifier]
if len(task) == 0:
abort(404)
return jsonify({'dataset': data_for_id})

Categories