json encoder different results for json.dump and json.dumps
json, python, python-3.x
Solution
The reason is here:
If you look into the source code of `json.__init__.py` in `CPython/Lib/json` here in github: https://github.com/python/cpython/blob/master/Lib/json/init.py
You'll find that `json.dump` actually use:
if (not skipkeys and ensure_ascii and
check_circular and allow_nan and
cls is None and indent is None and separators is None and
default is None and not sort_keys and not kw):
iterable = _default_encoder.iterencode(obj)
else:
if cls is None:
cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
default=default, sort_keys=sort_keys, **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at
# a debuggability cost
for chunk in iterable:
fp.write(chunk)
Hence the function you would want to `override` should be `json.JSONEncoder.iterencode` instead of `encode`.
Problem
I had a string in this format, ``` d = {'details': {'hawk_branch': {'tandem': ['4210bnd72']}, 'uclif_branch': {'tandem': ['e2nc712nma89', '23s24212', '12338cm82']}}} ``` I wanted to write it to file in this format, converting lists to dictionaries and adding word `value` as key for each value in the list, so `{'tandem': ['4210bnd72']}` should become ``` "tandem": { "value": "4210bnd72" } ``` Here is the expected output file, ``` { "details": { "hawk_branch": { "tandem": { "value": "4210bnd72" } }, "uclif_branch": { "tandem": { "value": "e2nc712nma89", "value": "23s24212", "value": "12338cm82", } } } } ``` I asked a question here where someone answered to use `json.JSONEncoder`, ``` class restore_value(json.JSONEncoder): def encode(self, o): if isinstance(o, dict): return '{%s}' % ', '.join(': '.join((json.encoder.py_encode_basestring(k), self.encode(v))) for k, v in o.items()) if isinstance(o, list): return '{%s}' % ', '.join('"value": %s' % self.encode(v) for v in o) return super().encode(o) ``` using above encoder, If the input is, ``` d = {'details': {'hawk_branch': {'tandem': ['4210bnd72']}, 'uclif_branch': {'tandem': ['e2nc712nma89', '23s24212', '12338cm82']}}} ``` the output will become, ``` print(json.dumps(d, cls=restore_value)) {"details": {"hawk_branch": {"tandem": {"value": "4210bnd72"}}, "uclif_branch": {"tandem": {"value": "e2nc712nma89", "value": "23s24212", "value": "12338cm82"}}}} ``` This is exactly what I wanted, but now I want to write it to a file. ``` with open("a.json", "w") as f: json.dump(d, f, cls=restore_value) ``` But it doesn't write in the same way as output by `json.dumps`. Expected output, ``` {"details": {"hawk_branch": {"tandem": {"value": "4210bnd72"}}, "uclif_branch": {"tandem": {"value": "e2nc712nma89", "value": "23s24212", "value": "12338cm82"}}}} ``` Output i am getting, ``` {"details": {"hawk_branch": {"tandem": ["4210bnd72"]}, "uclif_branch": {"tandem": ["e2nc712nma89", "23s24212", "12338cm82"]}}} ``` Can someone please tell me why its writing to a file differently even though I am using the encoder? Reproducing, Copy and run this using python 3, ``` import json class restore_value(json.JSONEncoder): def encode(self, o): if isinstance(o, dict): return '{%s}' % ', '.join(': '.join((json.encoder.py_encode_basestring(k), self.encode(v))) for k, v in o.items()) if isinstance(o, list): return '{%s}' % ', '.join('"value": %s' % self.encode(v) for v in o) return super().encode(o) d = {'details': {'hawk_branch': {'tandem': ['4210bnd72']}, 'uclif_branch': {'tandem': ['e2nc712nma89', '23s24212', '12338cm82']}}} print(json.dumps(d, cls=restore_value)) with open("a.json", "w") as f: json.dump(d, f, cls=restore_value) ```