Reputation: 3674
I am trying to write a JSONEncoder so I can dump some of my Python object data to JSON text to be read in by another application. I am having particular trouble converting the numpy arrays. I have read many of the SO articles on the subject, and I have borrowed heavily from @tlausch's solution in this article SimpleJSON and NumPy array - but I have not been able to get it working.
I have a pretty simple class that demonstrates my problem:
import numpy as np
class Movement:
def __init__(self, t1,t2,t3,t4):
self._t1 = t1
self._t2 = t2
self._t3 = t3
self._t4 = t4
self._a0 = np.array([0,0,0])
Here is the code from my JSONEncoder:
import json
import base64
import numpy as np
class MovementEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
if obj.flags['C_CONTIGUOUS']:
obj_data = obj.data
else:
cont_obj = np.ascontiguousarray(obj)
assert(cont_obj.flags['C_CONTIGUOUS'])
obj_data = cont_obj.data
data_b64 = base64.b64encode(obj_data)
return dict(__ndarray__ = data_b64, dtype = str(obj.dtype), shape = obj.shape)
try:
my_dict = obj.__dict__ ## <-- ERROR raised here
except TypeError:
pass
else:
return my_dict
return json.JSONEncoder.default(self, obj)
When I create a simple Movement object, and then call encoder.encode(obj), I get the following error:
>>> obj = Movement(1,2,3,4)
>>> encoder = MovementEncoder()
>>> encoder.encode(obj)
...
'bytes' object has no attribute '__dict__'
By adding some print statements to the encoder, I can see that it is correctly recursing from the object, to the object's dictionary, then to the attribute that has the np.array type. I thought the point of this solution was that the base64 representation of the ndarray
type was JSON encodable by default, but that appears not to be the case. Where did I go wrong?
Note: using Python 3.4, and NumPy 1.8.2
EDIT: updated code to show where error occurs
Upvotes: 2
Views: 4520
Reputation: 3674
I was finally able to solve this problem by adjusting the return value for numpy ndarray types to return a list, instead of a dict. I used the numpy builtin method tolist()
, which returns a json-encodable representation of an ndarray.
import json
import base64
import numpy as np
class MovementEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
if obj.flags['C_CONTIGUOUS']:
obj_data = obj.data
else:
cont_obj = np.ascontiguousarray(obj)
assert(cont_obj.flags['C_CONTIGUOUS'])
obj_data = cont_obj.data
## data_b64 = base64.b64encode(obj_data)
## converting to base64 and returning a dictionary did not work
## return dict(__ndarray__ = data_b64, dtype = str(obj.dtype), shape = obj.shape)
return obj.tolist() ## instead, utilize numpy builtin tolist() method
try:
my_dict = obj.__dict__ ## <-- ERROR raised here
except TypeError:
pass
else:
return my_dict
return json.JSONEncoder.default(self, obj)
Upvotes: 3