Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my custom JSONEncoder.default() ignoring booleans?

I want to convert a dictionary to a JSON string with boolean True values translated to the number 1 and boolean False values translated to the number 0. I'm using a JSONEncoder subclass, but it seems to ignore booleans ...

import json

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, bool):
            return 1 if obj else 0
    return super().default(obj)

data = { 'key-a' : 'a', 'key-true' : True, 'key-false' : False }

jsondata = json.dumps(data, cls=MyEncoder)

print(jsondata)

I want this to be the result:

{"key-true": 1, "key-a": "a", "key-false": 0}

However, this is what I get:

{"key-true": true, "key-a": "a", "key-false": false}

I know I can programatically modify the data before passing it to json.dumps, but is there any way I can obtain my desired result via a JSONEncoder subclass?

like image 542
HippoMan Avatar asked Sep 13 '17 18:09

HippoMan


People also ask

What JSON dumps do?

dumps() json. dumps() function converts a Python object into a json string. skipkeys: If skipkeys is True (default: False), then dict keys that are not of a basic type (str, int, float, bool, None) will be skipped instead of raising a TypeError.

Does JSON dump change order?

json. dump() will preserve the ordder of your dictionary. Open the file in a text editor and you will see. It will preserve the order regardless of whether you send it an OrderedDict.

What is FP in JSON dump?

A fp is a file pointer used to write JSON formatted data into file. Python json module always produces string objects, not bytes objects, therefore, fp.

What is JSON dumps and loads?

loads() takes in a string and returns a json object. json. dumps() takes in a json object and returns a string.


1 Answers

The default() method of JSONEncoder subclasses is called only when the encoder encounters an object it doesn't otherwise know how to serialize.

Unfortunately, the official documentation doesn't make this very clear. It's mentioned, but in the "keyword arguments" section for the class constructor, rather than in the documentation for the method:

If specified, default should be a function that gets called for objects that can’t otherwise be serialized. It should return a JSON encodable version of the object or raise a TypeError. If not specified, TypeError is raised.

This behaviour can easily be verified:

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, bool):
            print('got bool')
            return 1 if obj else 0
        if isinstance(obj, Foo):
            print('got Foo')
            return {'__Foo__': id(obj)}
        print('got unknown')
        return super().default(obj)

>>> class Foo: pass
...
>>> s = json.dumps({'a': False, 'b': True, 'c': Foo()}, cls=MyEncoder)
got Foo
>>> s
'{"a": false, "c": {"__Foo__": 140636444256856}, "b": true}'

JSONEncoder isn't designed to easily allow overriding the serialization of objects it already knows how to serialize (which is a good thing: the whole point of standards like JSON is that they're, well, standard) … so if you really want to encode booleans as though they were integers, the easiest way to do so is probably to preprocess data as suggested in your question.

like image 125
Zero Piraeus Avatar answered Oct 17 '22 21:10

Zero Piraeus