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

1

11

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?

Halhalafian answered 13/9, 2017 at 18:27 Comment(0)
F
24

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.

Fineberg answered 13/9, 2017 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.