Reputation: 6582
I have been experimenting with DynamoDB in AWS and have decided it's the wrong tool for me. While working in AWS, I uploaded a large number of records to the system that I am now trying to retrieve using Python. I admit, I'm completely new to Python and what I've cobbled together is from examples found around the internet. I'm working with Office 365 Audit logs that have many fields and when I retrieve them from Dynamo, I found that decimal fields are coming back as decimal('1')
.
{
'ItemType': 'File',
'SourceFileName': 'xxx',
'UserKey': 'xxx',
'RecordType': Decimal('6'),
'UserId': 'xxx',
'ClientIP': 'xxx',
'CorrelationId': 'xxx',
'ObjectId': 'xxx',
'Version': Decimal('1'),
'Site': 'xxx',
'WebId': 'xxx',
'SourceFileExtension': 'js',
'SiteUrl': 'xxx',
'Workload': 'SharePoint',
'SourceRelativeUrl': 'xxx',
'EventSource': 'SharePoint',
'ListId': 'xxx',
'OrganizationId': 'xxx',
'Operation': 'FileAccessed',
'UserAgent': 'xxx',
'ListItemUniqueId': 'xxx',
'CreationTime': '2019-07-26T21:54:45',
'UserType': Decimal('0'),
'Id': 'xxx',
'CustomUniqueId': False
}
In troubleshooting, I found this handy function that converts Decimal('0') to an actual decimal
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
if o % 1 > 0:
return float(o)
else:
return int(o)
return super(DecimalEncoder, self).default(o)
and this function is called
response = table.scan(Limit=10)
for i in response['Items']:
d = ast.literal_eval((json.dumps(i, cls=DecimalEncoder)))
For the most part this works fine, unless i has a boolean field (like the example above). Then I get this error
malformed node or string: <_ast.Name object at 0x04B3F778>
As far as I understand with Python, the boolean values of True and False are capitalized correctly.. I considered adding the the DecimalEncoder default method but this wouldn't do much since it's literally returning the same value
elif isinstance(o,bool):
return True if o else False
I did find by removing the key, I got no error
if 'CustomUniqueId' in i:
del i['CustomUniqueId']
However, don't want to lose this field. I'm at a loss as to how to resolve this.
Upvotes: 1
Views: 220
Reputation: 8097
The problem is that you are converting your Python object to a JSON string (i.e. the json.dumps
command) and then trying to interpret that string as Python dictionary (the ast.literal_eval
takes a string and tries to run it as Python code).
A python dictionary looks very similar to a JSON object but they are not 100% compatible. In JSON, boolean is presented as true
and false
and in Python it's represented as True
and False
.
I way to solve this would be to use json.loads
which reads a JSON string and converts it into a Python equivalent:
d = json.loads((json.dumps(i, cls=DecimalEncoder)))
That being said, this seems like a lot of hoops/processing to convert Decimal class to native floats and would recommend just using Decimal directly or converting to a float (i.e. float(d['RecordType'])
) if you need to pass them to something that expects a float.
Upvotes: 1