3

Here's a simple JSON encoder that convert float values to string:

class NestedEncoder(json.JSONEncoder):
    '''
    A JSON Encoder that converts floats/decimals to strings and allows nested objects
    '''

    def default(self, obj):

        if isinstance(obj, float) or obj.__class__.__name__ == "float32":
            return self.floattostr(obj)
        elif obj.__class__.__name__ == "type":
            return str(obj)
        elif hasattr(obj, 'repr_json'):
            return obj.repr_json()
        else:
            return json.JSONEncoder.default(self, obj)

    def floattostr(self,o,_inf=float('Inf'), _neginf=-float('-Inf'),nan_str="None"):
        if o != o:
            text = nan_str
        elif o == _inf:
            text = 'Infinity'
        elif o == _neginf:
            text = '-Infinity'
        else:
            return o.__repr__()

        return text

Now here's two tests. The first creates a Infinity valued float and encodes it using the custom encoder. Test passes.

def test_inf():
    inf = float('Inf')
    as_json = json.dumps(inf,cls=NestedEncoder)
    assert as_json == "Infinity"

The second test does the same but puts the float in a dictionary:

def test_inf_dic():
    inf = float('Inf')
    as_json = json.dumps({'key':inf},cls=NestedEncoder)
    assert as_json == "{'key':'Infinity'}"

OUTPUT:

=================================== FAILURES ===================================
_________________________________ test_inf_dic _________________________________

    def test_inf_dic():
        inf = float('Inf')
        as_json = json.dumps({'key':inf},cls=NestedEncoder)
>       assert as_json == "{'key':'Infinity'}"
E       assert '{"key": Infinity}' == "{'key':'Infinity'}"
E         - {"key": Infinity}
E         ?  ^   ^ ^
E         + {'key':'Infinity'}
E         ?  ^   ^ ^        +

EDIT:

The custom encoder only gets called for the first test, not the second.

2
  • Python2.7 returns "float" for float('Inf').__class__.__name__, so it won't match your first conditional. You are also trying to compare a string to a dict in your test. Commented Nov 14, 2017 at 1:36
  • @jordanm i fixed the assertion. See issue with encoding Infinity. The float('Inf').__class__.__name__ issue you pointed to isn't relevant since it's an OR conditional Commented Nov 14, 2017 at 1:40

1 Answer 1

1

You are correct that your custom function is not getting called during your test. The reason for this is explained in the docs:

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.

Since float is an object that it already knows how to serialized, it is not called. For float serialization, you can override the encode function.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks but if that was the case why does the first test passes?
@gidim json.dumps(float('Inf')) by default returns a string 'Infinity'. I am not sure why that is, but I tested it in ipython. I also tested your class and function by inserting a breakpoint that was never hit.
@gidim Looks like according to the json spec, Infinity can only be a value, which can only be inside of an object or array: tools.ietf.org/html/rfc7159
I think the spec doesn't allow Infinity at all, that's why i'm trying to convert it to a string. The first test does hit the NestedEncoder the second doesn't. Even if dic is supported it should get called on its values no?
@jordanm How does one go about overriding encode..?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.