I have a two-dimensional list like:
data = [[1,2,3], [2,3,4], [4,5,6]]
I want to write it to JSON file like this:
{
\'d
I thought you could use my answer to another similar question to do what you want. While it works with json.dumps()
, you pointed out that it doesn't for some reason with json.dump()
.
After looking into the matter, I discovered that the encode()
method of the derived json.JSONEncoder
that was being overridden in the linked answer is only called when dumps()
is called, but not when dump()
is called.
Fortunately, I quickly was able to determine the iterencode()
method does get invoked in both cases, so was able to fix the problem simply by more-or-less just moving the code from encode()
and putting it into this other method instead.
The code immediately below is revised version with this change in it:
from _ctypes import PyObj_FromPtr # see https://stackoverflow.com/a/15012814/355230
import json
import re
class NoIndent(object):
""" Value wrapper. """
def __init__(self, value):
if not isinstance(value, (list, tuple)):
raise TypeError('Only lists and tuples can be wrapped')
self.value = value
class MyEncoder(json.JSONEncoder):
FORMAT_SPEC = '@@{}@@' # Unique string pattern of NoIndent object ids.
regex = re.compile(FORMAT_SPEC.format(r'(\d+)')) # compile(r'@@(\d+)@@')
def __init__(self, **kwargs):
# Keyword arguments to ignore when encoding NoIndent wrapped values.
ignore = {'cls', 'indent'}
# Save copy of any keyword argument values needed for use here.
self._kwargs = {k: v for k, v in kwargs.items() if k not in ignore}
super(MyEncoder, self).__init__(**kwargs)
def default(self, obj):
return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, NoIndent)
else super(MyEncoder, self).default(obj))
def iterencode(self, obj, **kwargs):
format_spec = self.FORMAT_SPEC # Local var to expedite access.
# Replace any marked-up NoIndent wrapped values in the JSON repr
# with the json.dumps() of the corresponding wrapped Python object.
for encoded in super(MyEncoder, self).iterencode(obj, **kwargs):
match = self.regex.search(encoded)
if match:
id = int(match.group(1))
no_indent = PyObj_FromPtr(id)
json_repr = json.dumps(no_indent.value, **self._kwargs)
# Replace the matched id string with json formatted representation
# of the corresponding Python object.
encoded = encoded.replace(
'"{}"'.format(format_spec.format(id)), json_repr)
yield encoded
# Example of using it to do get the results you want.
alfa = [('a','b','c'), ('d','e','f'), ('g','h','i')]
data = [(1,2,3), (2,3,4), (4,5,6)]
data_struct = {
'data': [NoIndent(elem) for elem in data],
'alfa': [NoIndent(elem) for elem in alfa],
}
print(json.dumps(data_struct, cls=MyEncoder, sort_keys=True, indent=4))
# test custom JSONEncoder with json.dump()
with open('data_struct.json', 'w') as fp:
json.dump(data_struct, fp, cls=MyEncoder, sort_keys=True, indent=4)
fp.write('\n') # Add a newline to very end (optional).
data_struct.json
file):{
"alfa": [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"]
],
"data": [
[1, 2, 3],
[2, 3, 4],
[4, 5, 6]
]
}
You just need to add it to a empty dict as :
data = [[1,2,3], [2,3,4], [4,5,6]]
a = {}
a.update({"data":data})
print a
#{'data': [[1, 2, 3], [2, 3, 4], [4, 5, 6]]}
What you are trying in the 1st style is just a dict format. To get exact json from that dict You can add this dict to your json.dump to dump the file.
For json format you just need to dump it as :
import json
b = json.dumps(a)
print b
#{"data": [[1, 2, 3], [2, 3, 4], [4, 5, 6]]}
You can go to pro.jsonlint.com and check whether the json format is correct or not.