Python JSON serialize a Decimal object












180















I have a Decimal('3.9') as part of an object, and wish to encode this to a JSON string which should look like {'x': 3.9}. I don't care about precision on the client side, so a float is fine.



Is there a good way to serialize this? JSONDecoder doesn't accept Decimal objects, and converting to a float beforehand yields {'x': 3.8999999999999999} which is wrong, and will be a big waste of bandwidth.










share|improve this question

























  • related Python bug: json encoder unable to handle decimal

    – jfs
    Jul 27 '14 at 14:04











  • 3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

    – Jasen
    May 25 '15 at 3:55


















180















I have a Decimal('3.9') as part of an object, and wish to encode this to a JSON string which should look like {'x': 3.9}. I don't care about precision on the client side, so a float is fine.



Is there a good way to serialize this? JSONDecoder doesn't accept Decimal objects, and converting to a float beforehand yields {'x': 3.8999999999999999} which is wrong, and will be a big waste of bandwidth.










share|improve this question

























  • related Python bug: json encoder unable to handle decimal

    – jfs
    Jul 27 '14 at 14:04











  • 3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

    – Jasen
    May 25 '15 at 3:55
















180












180








180


40






I have a Decimal('3.9') as part of an object, and wish to encode this to a JSON string which should look like {'x': 3.9}. I don't care about precision on the client side, so a float is fine.



Is there a good way to serialize this? JSONDecoder doesn't accept Decimal objects, and converting to a float beforehand yields {'x': 3.8999999999999999} which is wrong, and will be a big waste of bandwidth.










share|improve this question
















I have a Decimal('3.9') as part of an object, and wish to encode this to a JSON string which should look like {'x': 3.9}. I don't care about precision on the client side, so a float is fine.



Is there a good way to serialize this? JSONDecoder doesn't accept Decimal objects, and converting to a float beforehand yields {'x': 3.8999999999999999} which is wrong, and will be a big waste of bandwidth.







python json floating-point decimal






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 7 '18 at 4:47









dreftymac

16.1k2191154




16.1k2191154










asked Dec 25 '09 at 5:00









KnioKnio

2,77631922




2,77631922













  • related Python bug: json encoder unable to handle decimal

    – jfs
    Jul 27 '14 at 14:04











  • 3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

    – Jasen
    May 25 '15 at 3:55





















  • related Python bug: json encoder unable to handle decimal

    – jfs
    Jul 27 '14 at 14:04











  • 3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

    – Jasen
    May 25 '15 at 3:55



















related Python bug: json encoder unable to handle decimal

– jfs
Jul 27 '14 at 14:04





related Python bug: json encoder unable to handle decimal

– jfs
Jul 27 '14 at 14:04













3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

– Jasen
May 25 '15 at 3:55







3.8999999999999999 is no more wrong than 3.4 is. 0.2 has no exact float representation.

– Jasen
May 25 '15 at 3:55














14 Answers
14






active

oldest

votes


















116














How about subclassing json.JSONEncoder?



class DecimalEncoder(json.JSONEncoder):
def _iterencode(self, o, markers=None):
if isinstance(o, decimal.Decimal):
# wanted a simple yield str(o) in the next line,
# but that would mean a yield on the line with super(...),
# which wouldn't work (see my comment below), so...
return (str(o) for o in [o])
return super(DecimalEncoder, self)._iterencode(o, markers)


Then use it like so:



json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)





share|improve this answer


























  • Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

    – Michał Marczyk
    Dec 25 '09 at 7:06











  • The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

    – Michał Marczyk
    Dec 25 '09 at 7:10






  • 8





    Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

    – mpen
    Aug 14 '11 at 23:09






  • 2





    @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

    – Abgan
    Jul 18 '13 at 10:08






  • 17





    This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

    – piro
    Mar 12 '14 at 17:17



















186














Simplejson 2.1 and higher has native support for Decimal type:



>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'


Note that use_decimal is True by default:



def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
encoding='utf-8', default=None, use_decimal=True,
namedtuple_as_object=True, tuple_as_array=True,
bigint_as_string=False, sort_keys=False, item_sort_key=None,
for_json=False, ignore_nan=False, **kw):


So:



>>> json.dumps(Decimal('3.9'))
'3.9'


Hopefully, this feature will be included in standard library.






share|improve this answer





















  • 1





    easiest fix! thanks for the answer!

    – cce
    Jul 30 '10 at 23:17






  • 6





    Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

    – Matthew Schinckel
    Oct 22 '10 at 0:12






  • 11





    @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

    – Shekhar
    Nov 22 '11 at 10:55








  • 1





    Aha, I think I was not using use_decimal=True on the loads, too.

    – Matthew Schinckel
    Nov 23 '11 at 23:04






  • 3





    simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

    – jfs
    Jul 27 '14 at 14:04



















139














I would like to let everyone know that I tried Michał Marczyk's answer on my web server that was running Python 2.6.5 and it worked fine. However, I upgraded to Python 2.7 and it stopped working. I tried to think of some sort of way to encode Decimal objects and this is what I came up with:



class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
return float(o)
return super(DecimalEncoder, self).default(o)


This should hopefully help anyone who is having problems with Python 2.7. I tested it and it seems to work fine. If anyone notices any bugs in my solution or comes up with a better way, please let me know.






share|improve this answer



















  • 4





    Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

    – Nelson
    Apr 6 '11 at 23:32











  • Same with Python 3.2

    – Teifion
    Feb 4 '13 at 0:12






  • 2





    For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

    – Joel Cross
    Oct 28 '13 at 15:42






  • 9





    Use unicode or str instead of float to ensure precision.

    – Seppo Erviälä
    Aug 30 '16 at 13:03








  • 2





    The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

    – hynekcer
    Jun 16 '17 at 20:25



















25














I tried switching from simplejson to builtin json for GAE 2.7, and had issues with the decimal. If default returned str(o) there were quotes (because _iterencode calls _iterencode on the results of default), and float(o) would remove trailing 0.



If default returns an object of a class that inherits from float (or anything that calls repr without additional formatting) and has a custom __repr__ method, it seems to work like I want it to.



import json
from decimal import Decimal

class fakefloat(float):
def __init__(self, value):
self._value = value
def __repr__(self):
return str(self._value)

def defaultencode(o):
if isinstance(o, Decimal):
# Subclass float with custom repr?
return fakefloat(o)
raise TypeError(repr(o) + " is not JSON serializable")

json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
'[10.2, "10.20", 10.20]'





share|improve this answer
























  • Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

    – konrad
    Apr 6 '13 at 13:36











  • although works fine locally, breaks in GAE. both using 2.7

    – sha256
    Feb 19 '14 at 13:22











  • @sha256 Can you show a traceback of how it breaks?

    – Matthew Schinckel
    Feb 12 '16 at 1:09






  • 3





    Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

    – Antti Haapala
    Jan 15 '17 at 21:52











  • Hilarious magic, worked with 2.7.11 on Win x64 :)

    – mlvljr
    Feb 22 '17 at 22:38



















23














In my Flask app, Which uses python 2.7.11, flask alchemy(with 'db.decimal' types), and Flask Marshmallow ( for 'instant' serializer and deserializer), i had this error, every time i did a GET or POST. The serializer and deserializer, failed to convert Decimal types into any JSON identifiable format.



I did a "pip install simplejson", then
Just by adding



import simplejson as json


the serializer and deserializer starts to purr again. I did nothing else...
DEciamls are displayed as '234.00' float format.






share|improve this answer
























  • the easiest fix

    – SMDC
    May 30 '18 at 2:08



















12














3.9 can not be exactly represented in IEEE floats, it will always come as 3.8999999999999999, e.g. try print repr(3.9), you can read more about it here:



http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html



So if you don't want float, only option you have to send it as string, and to allow automatic conversion of decimal objects to JSON, do something like this:



import decimal
from django.utils import simplejson

def json_encode_decimal(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
raise TypeError(repr(obj) + " is not JSON serializable")

d = decimal.Decimal('3.5')
print simplejson.dumps([d], default=json_encode_decimal)





share|improve this answer


























  • I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

    – Knio
    Dec 25 '09 at 5:32











  • @Anurag You meant repr(obj) instead of repr(o) in your example.

    – orokusaki
    Feb 15 '10 at 0:09











  • @orokusaki, yes thanks.

    – Anurag Uniyal
    Feb 15 '10 at 4:49






  • 1





    @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

    – Anurag Uniyal
    Aug 5 '11 at 14:24






  • 1





    See mikez302's answer - in Python 2.7 or above, this no longer applies.

    – Joel Cross
    Oct 28 '13 at 15:42



















10














The native option is missing so I'll add it for the next guy/gall that looks for it.



Starting on Django 1.7.x there is a built-in DjangoJSONEncoder that you can get it from django.core.serializers.json.



import json
from django.core.serializers.json import DjangoJSONEncoder
from django.forms.models import model_to_dict

model_instance = YourModel.object.first()
model_dict = model_to_dict(model_instance)

json.dumps(model_dict, cls=DjangoJSONEncoder)


Presto!






share|improve this answer
























  • Although this is great to know, the OP didn't ask about Django?

    – std''OrgnlDave
    Jul 25 '18 at 14:11






  • 1





    @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

    – Javier Buzzi
    Jul 25 '18 at 15:55






  • 1





    always good to prep for Googlers

    – std''OrgnlDave
    Jul 26 '18 at 20:07






  • 1





    you save my day

    – gaozhidf
    Feb 19 at 1:36



















9














My $.02!



I extend a bunch of the JSON encoder since I am serializing tons of data for my web server. Here's some nice code. Note that it's easily extendable to pretty much any data format you feel like and will reproduce 3.9 as "thing": 3.9



JSONEncoder_olddefault = json.JSONEncoder.default
def JSONEncoder_newdefault(self, o):
if isinstance(o, UUID): return str(o)
if isinstance(o, datetime): return str(o)
if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
if isinstance(o, decimal.Decimal): return str(o)
return JSONEncoder_olddefault(self, o)
json.JSONEncoder.default = JSONEncoder_newdefault


Makes my life so much easier...






share|improve this answer



















  • 3





    This is incorrect: it will reproduce 3.9 as "thing": "3.9".

    – Glyph
    Dec 13 '17 at 22:24











  • the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

    – stackdave
    Jan 17 '18 at 15:30













  • @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

    – std''OrgnlDave
    Jan 19 '18 at 19:02






  • 2





    do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

    – Glyph
    Jan 20 '18 at 23:29



















6














This is what I have, extracted from our class



class CommonJSONEncoder(json.JSONEncoder):

"""
Common JSON Encoder
json.dumps(myString, cls=CommonJSONEncoder)
"""

def default(self, obj):

if isinstance(obj, decimal.Decimal):
return {'type{decimal}': str(obj)}

class CommonJSONDecoder(json.JSONDecoder):

"""
Common JSON Encoder
json.loads(myString, cls=CommonJSONEncoder)
"""

@classmethod
def object_hook(cls, obj):
for key in obj:
if isinstance(key, six.string_types):
if 'type{decimal}' == key:
try:
return decimal.Decimal(obj[key])
except:
pass

def __init__(self, **kwargs):
kwargs['object_hook'] = self.object_hook
super(CommonJSONDecoder, self).__init__(**kwargs)


Which passes unittest:



def test_encode_and_decode_decimal(self):
obj = Decimal('1.11')
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)

obj = {'test': Decimal('1.11')}
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)

obj = {'test': {'abc': Decimal('1.11')}}
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)





share|improve this answer
























  • json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

    – Can Kavaklıoğlu
    Dec 6 '15 at 14:54













  • object_hook needs a default return value if obj is not decimal.

    – Can Kavaklıoğlu
    Feb 1 '16 at 13:14













  • What is "six.string_types" in your decoder?

    – warrens
    Dec 12 '18 at 20:42











  • @warrens pypi.org/project/six

    – James Lin
    Dec 12 '18 at 20:48



















2














Based on stdOrgnlDave answer I have defined this wrapper that it can be called with optional kinds so the encoder will work only for certain kinds inside your projects. I believe the work should be done inside your code and not to use this "default" encoder since "it is better explicit than implicit", but I understand using this will save some of your time. :-)



import time
import json
import decimal
from uuid import UUID
from datetime import datetime

def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
'''
JSON Encoder newdfeault is a wrapper capable of encoding several kinds
Use it anywhere on your code to make the full system to work with this defaults:
JSONEncoder_newdefault() # for everything
JSONEncoder_newdefault(['decimal']) # only for Decimal
'''
JSONEncoder_olddefault = json.JSONEncoder.default

def JSONEncoder_wrapped(self, o):
'''
json.JSONEncoder.default = JSONEncoder_newdefault
'''
if ('uuid' in kind) and isinstance(o, uuid.UUID):
return str(o)
if ('datetime' in kind) and isinstance(o, datetime):
return str(o)
if ('time' in kind) and isinstance(o, time.struct_time):
return datetime.fromtimestamp(time.mktime(o))
if ('decimal' in kind) and isinstance(o, decimal.Decimal):
return str(o)
return JSONEncoder_olddefault(self, o)
json.JSONEncoder.default = JSONEncoder_wrapped

# Example
if __name__ == '__main__':
JSONEncoder_newdefault()





share|improve this answer































    2














    From the JSON Standard Document, as linked in json.org:




    JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of
    number types of various capacities and complements, fixed or floating, binary or decimal. That can make
    interchange between different programming languages difficult. JSON instead offers only the representation of
    numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
    sequences even if they disagree on internal representations. That is enough to allow interchange.




    So it's actually accurate to represent Decimals as numbers (rather than strings) in JSON. Bellow lies a possible solution to the problem.



    Define a custom JSON encoder:



    import json


    class CustomJsonEncoder(json.JSONEncoder):

    def default(self, obj):
    if isinstance(obj, Decimal):
    return float(obj)
    return super(CustomJsonEncoder, self).default(obj)


    Then use it when serializing your data:



    json.dumps(data, cls=CustomJsonEncoder)


    As noted from comments on the other answers, older versions of python might mess up the representation when converting to float, but that's not the case anymore.



    To get the decimal back in Python:



    Decimal(str(value))


    This solution is hinted in Python 3.0 documentation on decimals:




    To create a Decimal from a float, first convert it to a string.







    share|improve this answer





















    • 1





      This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

      – juanpa.arrivillaga
      Jan 1 at 19:58













    • I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

      – hugo_leonardo
      Jan 26 at 19:51











    • With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

      – hugo_leonardo
      Jan 26 at 19:53



















    0














    If you want to pass a dictionary containing decimals to the requests library (using the json keyword argument), you simply need to install simplejson:



    $ pip3 install simplejson    
    $ python3
    >>> import requests
    >>> from decimal import Decimal
    >>> # This won't error out:
    >>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})


    The reason of the problem is that requests uses simplejson only if it is present, and falls back to the built-in json if it is not installed.






    share|improve this answer































      0














      You can create a custom JSON encoder as per your requirement.



      import json
      from datetime import datetime, date
      from time import time, struct_time, mktime
      import decimal

      class CustomJSONEncoder(json.JSONEncoder):
      def default(self, o):
      if isinstance(o, datetime):
      return str(o)
      if isinstance(o, date):
      return str(o)
      if isinstance(o, decimal.Decimal):
      return float(o)
      if isinstance(o, struct_time):
      return datetime.fromtimestamp(mktime(o))
      # Any other serializer if needed
      return super(CustomJSONEncoder, self).default(o)


      The Decoder can be called like this,



      import json
      from decimal import Decimal
      json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)


      and the output will be:



      >>'{"x": 3.9}'





      share|improve this answer































        -5














        this can be done by adding



            elif isinstance(o, decimal.Decimal):
        yield str(o)


        in Libjsonencoder.py:JSONEncoder._iterencode, but I was hoping for a better solution






        share|improve this answer



















        • 3





          You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

          – justanr
          Jan 17 '15 at 22:11











        Your Answer






        StackExchange.ifUsing("editor", function () {
        StackExchange.using("externalEditor", function () {
        StackExchange.using("snippets", function () {
        StackExchange.snippets.init();
        });
        });
        }, "code-snippets");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "1"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f1960516%2fpython-json-serialize-a-decimal-object%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        14 Answers
        14






        active

        oldest

        votes








        14 Answers
        14






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        116














        How about subclassing json.JSONEncoder?



        class DecimalEncoder(json.JSONEncoder):
        def _iterencode(self, o, markers=None):
        if isinstance(o, decimal.Decimal):
        # wanted a simple yield str(o) in the next line,
        # but that would mean a yield on the line with super(...),
        # which wouldn't work (see my comment below), so...
        return (str(o) for o in [o])
        return super(DecimalEncoder, self)._iterencode(o, markers)


        Then use it like so:



        json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)





        share|improve this answer


























        • Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

          – Michał Marczyk
          Dec 25 '09 at 7:06











        • The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

          – Michał Marczyk
          Dec 25 '09 at 7:10






        • 8





          Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

          – mpen
          Aug 14 '11 at 23:09






        • 2





          @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

          – Abgan
          Jul 18 '13 at 10:08






        • 17





          This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

          – piro
          Mar 12 '14 at 17:17
















        116














        How about subclassing json.JSONEncoder?



        class DecimalEncoder(json.JSONEncoder):
        def _iterencode(self, o, markers=None):
        if isinstance(o, decimal.Decimal):
        # wanted a simple yield str(o) in the next line,
        # but that would mean a yield on the line with super(...),
        # which wouldn't work (see my comment below), so...
        return (str(o) for o in [o])
        return super(DecimalEncoder, self)._iterencode(o, markers)


        Then use it like so:



        json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)





        share|improve this answer


























        • Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

          – Michał Marczyk
          Dec 25 '09 at 7:06











        • The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

          – Michał Marczyk
          Dec 25 '09 at 7:10






        • 8





          Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

          – mpen
          Aug 14 '11 at 23:09






        • 2





          @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

          – Abgan
          Jul 18 '13 at 10:08






        • 17





          This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

          – piro
          Mar 12 '14 at 17:17














        116












        116








        116







        How about subclassing json.JSONEncoder?



        class DecimalEncoder(json.JSONEncoder):
        def _iterencode(self, o, markers=None):
        if isinstance(o, decimal.Decimal):
        # wanted a simple yield str(o) in the next line,
        # but that would mean a yield on the line with super(...),
        # which wouldn't work (see my comment below), so...
        return (str(o) for o in [o])
        return super(DecimalEncoder, self)._iterencode(o, markers)


        Then use it like so:



        json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)





        share|improve this answer















        How about subclassing json.JSONEncoder?



        class DecimalEncoder(json.JSONEncoder):
        def _iterencode(self, o, markers=None):
        if isinstance(o, decimal.Decimal):
        # wanted a simple yield str(o) in the next line,
        # but that would mean a yield on the line with super(...),
        # which wouldn't work (see my comment below), so...
        return (str(o) for o in [o])
        return super(DecimalEncoder, self)._iterencode(o, markers)


        Then use it like so:



        json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 25 '09 at 7:14

























        answered Dec 25 '09 at 6:43









        Michał MarczykMichał Marczyk

        75.7k9178197




        75.7k9178197













        • Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

          – Michał Marczyk
          Dec 25 '09 at 7:06











        • The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

          – Michał Marczyk
          Dec 25 '09 at 7:10






        • 8





          Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

          – mpen
          Aug 14 '11 at 23:09






        • 2





          @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

          – Abgan
          Jul 18 '13 at 10:08






        • 17





          This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

          – piro
          Mar 12 '14 at 17:17



















        • Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

          – Michał Marczyk
          Dec 25 '09 at 7:06











        • The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

          – Michał Marczyk
          Dec 25 '09 at 7:10






        • 8





          Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

          – mpen
          Aug 14 '11 at 23:09






        • 2





          @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

          – Abgan
          Jul 18 '13 at 10:08






        • 17





          This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

          – piro
          Mar 12 '14 at 17:17

















        Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

        – Michał Marczyk
        Dec 25 '09 at 7:06





        Ouch, I just noticed that it won't actually work like this. Will edit accordingly. (The idea stays the same, though.)

        – Michał Marczyk
        Dec 25 '09 at 7:06













        The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

        – Michał Marczyk
        Dec 25 '09 at 7:10





        The problem was that DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next() returned the correct '3.9', but DecimalEncoder()._iterencode(3.9).next() returned a generator object which would only return '3.899...' when you piled on another .next(). Generator funny business. Oh well... Should work now.

        – Michał Marczyk
        Dec 25 '09 at 7:10




        8




        8





        Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

        – mpen
        Aug 14 '11 at 23:09





        Can't you just return (str(o),) instead? [o] is a list with only 1 element, why bother looping over it?

        – mpen
        Aug 14 '11 at 23:09




        2




        2





        @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

        – Abgan
        Jul 18 '13 at 10:08





        @Mark: return (str(o),) would return tuple of length 1, while code in the answer returns generator of length 1. See the iterencode() docs

        – Abgan
        Jul 18 '13 at 10:08




        17




        17





        This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

        – piro
        Mar 12 '14 at 17:17





        This implementation doesn't work anymore. Elias Zamaria's one is the one working on the same style.

        – piro
        Mar 12 '14 at 17:17













        186














        Simplejson 2.1 and higher has native support for Decimal type:



        >>> json.dumps(Decimal('3.9'), use_decimal=True)
        '3.9'


        Note that use_decimal is True by default:



        def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        encoding='utf-8', default=None, use_decimal=True,
        namedtuple_as_object=True, tuple_as_array=True,
        bigint_as_string=False, sort_keys=False, item_sort_key=None,
        for_json=False, ignore_nan=False, **kw):


        So:



        >>> json.dumps(Decimal('3.9'))
        '3.9'


        Hopefully, this feature will be included in standard library.






        share|improve this answer





















        • 1





          easiest fix! thanks for the answer!

          – cce
          Jul 30 '10 at 23:17






        • 6





          Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

          – Matthew Schinckel
          Oct 22 '10 at 0:12






        • 11





          @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

          – Shekhar
          Nov 22 '11 at 10:55








        • 1





          Aha, I think I was not using use_decimal=True on the loads, too.

          – Matthew Schinckel
          Nov 23 '11 at 23:04






        • 3





          simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

          – jfs
          Jul 27 '14 at 14:04
















        186














        Simplejson 2.1 and higher has native support for Decimal type:



        >>> json.dumps(Decimal('3.9'), use_decimal=True)
        '3.9'


        Note that use_decimal is True by default:



        def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        encoding='utf-8', default=None, use_decimal=True,
        namedtuple_as_object=True, tuple_as_array=True,
        bigint_as_string=False, sort_keys=False, item_sort_key=None,
        for_json=False, ignore_nan=False, **kw):


        So:



        >>> json.dumps(Decimal('3.9'))
        '3.9'


        Hopefully, this feature will be included in standard library.






        share|improve this answer





















        • 1





          easiest fix! thanks for the answer!

          – cce
          Jul 30 '10 at 23:17






        • 6





          Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

          – Matthew Schinckel
          Oct 22 '10 at 0:12






        • 11





          @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

          – Shekhar
          Nov 22 '11 at 10:55








        • 1





          Aha, I think I was not using use_decimal=True on the loads, too.

          – Matthew Schinckel
          Nov 23 '11 at 23:04






        • 3





          simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

          – jfs
          Jul 27 '14 at 14:04














        186












        186








        186







        Simplejson 2.1 and higher has native support for Decimal type:



        >>> json.dumps(Decimal('3.9'), use_decimal=True)
        '3.9'


        Note that use_decimal is True by default:



        def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        encoding='utf-8', default=None, use_decimal=True,
        namedtuple_as_object=True, tuple_as_array=True,
        bigint_as_string=False, sort_keys=False, item_sort_key=None,
        for_json=False, ignore_nan=False, **kw):


        So:



        >>> json.dumps(Decimal('3.9'))
        '3.9'


        Hopefully, this feature will be included in standard library.






        share|improve this answer















        Simplejson 2.1 and higher has native support for Decimal type:



        >>> json.dumps(Decimal('3.9'), use_decimal=True)
        '3.9'


        Note that use_decimal is True by default:



        def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        encoding='utf-8', default=None, use_decimal=True,
        namedtuple_as_object=True, tuple_as_array=True,
        bigint_as_string=False, sort_keys=False, item_sort_key=None,
        for_json=False, ignore_nan=False, **kw):


        So:



        >>> json.dumps(Decimal('3.9'))
        '3.9'


        Hopefully, this feature will be included in standard library.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Feb 23 '17 at 16:36









        Jonah Bishop

        9,05433357




        9,05433357










        answered Jun 30 '10 at 10:34









        Lukas CenovskyLukas Cenovsky

        4,00722237




        4,00722237








        • 1





          easiest fix! thanks for the answer!

          – cce
          Jul 30 '10 at 23:17






        • 6





          Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

          – Matthew Schinckel
          Oct 22 '10 at 0:12






        • 11





          @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

          – Shekhar
          Nov 22 '11 at 10:55








        • 1





          Aha, I think I was not using use_decimal=True on the loads, too.

          – Matthew Schinckel
          Nov 23 '11 at 23:04






        • 3





          simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

          – jfs
          Jul 27 '14 at 14:04














        • 1





          easiest fix! thanks for the answer!

          – cce
          Jul 30 '10 at 23:17






        • 6





          Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

          – Matthew Schinckel
          Oct 22 '10 at 0:12






        • 11





          @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

          – Shekhar
          Nov 22 '11 at 10:55








        • 1





          Aha, I think I was not using use_decimal=True on the loads, too.

          – Matthew Schinckel
          Nov 23 '11 at 23:04






        • 3





          simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

          – jfs
          Jul 27 '14 at 14:04








        1




        1





        easiest fix! thanks for the answer!

        – cce
        Jul 30 '10 at 23:17





        easiest fix! thanks for the answer!

        – cce
        Jul 30 '10 at 23:17




        6




        6





        Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

        – Matthew Schinckel
        Oct 22 '10 at 0:12





        Hmm, for me this converts Decimal objects to floats, which is not acceptable. Loss of precision when working with currency, for instance.

        – Matthew Schinckel
        Oct 22 '10 at 0:12




        11




        11





        @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

        – Shekhar
        Nov 22 '11 at 10:55







        @MatthewSchinckel I think it doesn't. It actually makes a string out of it. And if you feed the resultant string back to json.loads(s, use_decimal=True) it gives you back the decimal. No float in entire process. Edited above answer. Hope original poster is fine with it.

        – Shekhar
        Nov 22 '11 at 10:55






        1




        1





        Aha, I think I was not using use_decimal=True on the loads, too.

        – Matthew Schinckel
        Nov 23 '11 at 23:04





        Aha, I think I was not using use_decimal=True on the loads, too.

        – Matthew Schinckel
        Nov 23 '11 at 23:04




        3




        3





        simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

        – jfs
        Jul 27 '14 at 14:04





        simplejson.dumps(decimal.Decimal('2.2')) also works: no explicit use_decimal (tested on simplejson/3.6.0). Another way to load it back is: json.loads(s, parse_float=Decimal) i.e., you can read it using stdlib json (and old simplejson versions are also supported).

        – jfs
        Jul 27 '14 at 14:04











        139














        I would like to let everyone know that I tried Michał Marczyk's answer on my web server that was running Python 2.6.5 and it worked fine. However, I upgraded to Python 2.7 and it stopped working. I tried to think of some sort of way to encode Decimal objects and this is what I came up with:



        class DecimalEncoder(json.JSONEncoder):
        def default(self, o):
        if isinstance(o, decimal.Decimal):
        return float(o)
        return super(DecimalEncoder, self).default(o)


        This should hopefully help anyone who is having problems with Python 2.7. I tested it and it seems to work fine. If anyone notices any bugs in my solution or comes up with a better way, please let me know.






        share|improve this answer



















        • 4





          Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

          – Nelson
          Apr 6 '11 at 23:32











        • Same with Python 3.2

          – Teifion
          Feb 4 '13 at 0:12






        • 2





          For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

          – Joel Cross
          Oct 28 '13 at 15:42






        • 9





          Use unicode or str instead of float to ensure precision.

          – Seppo Erviälä
          Aug 30 '16 at 13:03








        • 2





          The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

          – hynekcer
          Jun 16 '17 at 20:25
















        139














        I would like to let everyone know that I tried Michał Marczyk's answer on my web server that was running Python 2.6.5 and it worked fine. However, I upgraded to Python 2.7 and it stopped working. I tried to think of some sort of way to encode Decimal objects and this is what I came up with:



        class DecimalEncoder(json.JSONEncoder):
        def default(self, o):
        if isinstance(o, decimal.Decimal):
        return float(o)
        return super(DecimalEncoder, self).default(o)


        This should hopefully help anyone who is having problems with Python 2.7. I tested it and it seems to work fine. If anyone notices any bugs in my solution or comes up with a better way, please let me know.






        share|improve this answer



















        • 4





          Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

          – Nelson
          Apr 6 '11 at 23:32











        • Same with Python 3.2

          – Teifion
          Feb 4 '13 at 0:12






        • 2





          For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

          – Joel Cross
          Oct 28 '13 at 15:42






        • 9





          Use unicode or str instead of float to ensure precision.

          – Seppo Erviälä
          Aug 30 '16 at 13:03








        • 2





          The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

          – hynekcer
          Jun 16 '17 at 20:25














        139












        139








        139







        I would like to let everyone know that I tried Michał Marczyk's answer on my web server that was running Python 2.6.5 and it worked fine. However, I upgraded to Python 2.7 and it stopped working. I tried to think of some sort of way to encode Decimal objects and this is what I came up with:



        class DecimalEncoder(json.JSONEncoder):
        def default(self, o):
        if isinstance(o, decimal.Decimal):
        return float(o)
        return super(DecimalEncoder, self).default(o)


        This should hopefully help anyone who is having problems with Python 2.7. I tested it and it seems to work fine. If anyone notices any bugs in my solution or comes up with a better way, please let me know.






        share|improve this answer













        I would like to let everyone know that I tried Michał Marczyk's answer on my web server that was running Python 2.6.5 and it worked fine. However, I upgraded to Python 2.7 and it stopped working. I tried to think of some sort of way to encode Decimal objects and this is what I came up with:



        class DecimalEncoder(json.JSONEncoder):
        def default(self, o):
        if isinstance(o, decimal.Decimal):
        return float(o)
        return super(DecimalEncoder, self).default(o)


        This should hopefully help anyone who is having problems with Python 2.7. I tested it and it seems to work fine. If anyone notices any bugs in my solution or comes up with a better way, please let me know.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Oct 7 '10 at 19:29









        Elias ZamariaElias Zamaria

        42.9k2589128




        42.9k2589128








        • 4





          Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

          – Nelson
          Apr 6 '11 at 23:32











        • Same with Python 3.2

          – Teifion
          Feb 4 '13 at 0:12






        • 2





          For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

          – Joel Cross
          Oct 28 '13 at 15:42






        • 9





          Use unicode or str instead of float to ensure precision.

          – Seppo Erviälä
          Aug 30 '16 at 13:03








        • 2





          The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

          – hynekcer
          Jun 16 '17 at 20:25














        • 4





          Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

          – Nelson
          Apr 6 '11 at 23:32











        • Same with Python 3.2

          – Teifion
          Feb 4 '13 at 0:12






        • 2





          For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

          – Joel Cross
          Oct 28 '13 at 15:42






        • 9





          Use unicode or str instead of float to ensure precision.

          – Seppo Erviälä
          Aug 30 '16 at 13:03








        • 2





          The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

          – hynekcer
          Jun 16 '17 at 20:25








        4




        4





        Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

        – Nelson
        Apr 6 '11 at 23:32





        Python 2.7 changed the rules for rounding floats so this works. See discussion in stackoverflow.com/questions/1447287/…

        – Nelson
        Apr 6 '11 at 23:32













        Same with Python 3.2

        – Teifion
        Feb 4 '13 at 0:12





        Same with Python 3.2

        – Teifion
        Feb 4 '13 at 0:12




        2




        2





        For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

        – Joel Cross
        Oct 28 '13 at 15:42





        For those of us who can't use simplejson (ie. on Google App Engine) this answer is a Godsend.

        – Joel Cross
        Oct 28 '13 at 15:42




        9




        9





        Use unicode or str instead of float to ensure precision.

        – Seppo Erviälä
        Aug 30 '16 at 13:03







        Use unicode or str instead of float to ensure precision.

        – Seppo Erviälä
        Aug 30 '16 at 13:03






        2




        2





        The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

        – hynekcer
        Jun 16 '17 at 20:25





        The problem with 54.3999... was important in Python 2.6.x and older where the conversion float to string did not worked regularly, but the conversion Decimal to str is much more incorrect because it would be serialized as string with double quotes "54.4", not as a number.

        – hynekcer
        Jun 16 '17 at 20:25











        25














        I tried switching from simplejson to builtin json for GAE 2.7, and had issues with the decimal. If default returned str(o) there were quotes (because _iterencode calls _iterencode on the results of default), and float(o) would remove trailing 0.



        If default returns an object of a class that inherits from float (or anything that calls repr without additional formatting) and has a custom __repr__ method, it seems to work like I want it to.



        import json
        from decimal import Decimal

        class fakefloat(float):
        def __init__(self, value):
        self._value = value
        def __repr__(self):
        return str(self._value)

        def defaultencode(o):
        if isinstance(o, Decimal):
        # Subclass float with custom repr?
        return fakefloat(o)
        raise TypeError(repr(o) + " is not JSON serializable")

        json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
        '[10.2, "10.20", 10.20]'





        share|improve this answer
























        • Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

          – konrad
          Apr 6 '13 at 13:36











        • although works fine locally, breaks in GAE. both using 2.7

          – sha256
          Feb 19 '14 at 13:22











        • @sha256 Can you show a traceback of how it breaks?

          – Matthew Schinckel
          Feb 12 '16 at 1:09






        • 3





          Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

          – Antti Haapala
          Jan 15 '17 at 21:52











        • Hilarious magic, worked with 2.7.11 on Win x64 :)

          – mlvljr
          Feb 22 '17 at 22:38
















        25














        I tried switching from simplejson to builtin json for GAE 2.7, and had issues with the decimal. If default returned str(o) there were quotes (because _iterencode calls _iterencode on the results of default), and float(o) would remove trailing 0.



        If default returns an object of a class that inherits from float (or anything that calls repr without additional formatting) and has a custom __repr__ method, it seems to work like I want it to.



        import json
        from decimal import Decimal

        class fakefloat(float):
        def __init__(self, value):
        self._value = value
        def __repr__(self):
        return str(self._value)

        def defaultencode(o):
        if isinstance(o, Decimal):
        # Subclass float with custom repr?
        return fakefloat(o)
        raise TypeError(repr(o) + " is not JSON serializable")

        json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
        '[10.2, "10.20", 10.20]'





        share|improve this answer
























        • Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

          – konrad
          Apr 6 '13 at 13:36











        • although works fine locally, breaks in GAE. both using 2.7

          – sha256
          Feb 19 '14 at 13:22











        • @sha256 Can you show a traceback of how it breaks?

          – Matthew Schinckel
          Feb 12 '16 at 1:09






        • 3





          Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

          – Antti Haapala
          Jan 15 '17 at 21:52











        • Hilarious magic, worked with 2.7.11 on Win x64 :)

          – mlvljr
          Feb 22 '17 at 22:38














        25












        25








        25







        I tried switching from simplejson to builtin json for GAE 2.7, and had issues with the decimal. If default returned str(o) there were quotes (because _iterencode calls _iterencode on the results of default), and float(o) would remove trailing 0.



        If default returns an object of a class that inherits from float (or anything that calls repr without additional formatting) and has a custom __repr__ method, it seems to work like I want it to.



        import json
        from decimal import Decimal

        class fakefloat(float):
        def __init__(self, value):
        self._value = value
        def __repr__(self):
        return str(self._value)

        def defaultencode(o):
        if isinstance(o, Decimal):
        # Subclass float with custom repr?
        return fakefloat(o)
        raise TypeError(repr(o) + " is not JSON serializable")

        json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
        '[10.2, "10.20", 10.20]'





        share|improve this answer













        I tried switching from simplejson to builtin json for GAE 2.7, and had issues with the decimal. If default returned str(o) there were quotes (because _iterencode calls _iterencode on the results of default), and float(o) would remove trailing 0.



        If default returns an object of a class that inherits from float (or anything that calls repr without additional formatting) and has a custom __repr__ method, it seems to work like I want it to.



        import json
        from decimal import Decimal

        class fakefloat(float):
        def __init__(self, value):
        self._value = value
        def __repr__(self):
        return str(self._value)

        def defaultencode(o):
        if isinstance(o, Decimal):
        # Subclass float with custom repr?
        return fakefloat(o)
        raise TypeError(repr(o) + " is not JSON serializable")

        json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
        '[10.2, "10.20", 10.20]'






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 25 '11 at 21:17









        tesdaltesdal

        2,2191210




        2,2191210













        • Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

          – konrad
          Apr 6 '13 at 13:36











        • although works fine locally, breaks in GAE. both using 2.7

          – sha256
          Feb 19 '14 at 13:22











        • @sha256 Can you show a traceback of how it breaks?

          – Matthew Schinckel
          Feb 12 '16 at 1:09






        • 3





          Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

          – Antti Haapala
          Jan 15 '17 at 21:52











        • Hilarious magic, worked with 2.7.11 on Win x64 :)

          – mlvljr
          Feb 22 '17 at 22:38



















        • Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

          – konrad
          Apr 6 '13 at 13:36











        • although works fine locally, breaks in GAE. both using 2.7

          – sha256
          Feb 19 '14 at 13:22











        • @sha256 Can you show a traceback of how it breaks?

          – Matthew Schinckel
          Feb 12 '16 at 1:09






        • 3





          Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

          – Antti Haapala
          Jan 15 '17 at 21:52











        • Hilarious magic, worked with 2.7.11 on Win x64 :)

          – mlvljr
          Feb 22 '17 at 22:38

















        Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

        – konrad
        Apr 6 '13 at 13:36





        Nice! This makes sure the decimal value ends up in the JSON as a Javascript float, without having Python first round it to the nearest float value.

        – konrad
        Apr 6 '13 at 13:36













        although works fine locally, breaks in GAE. both using 2.7

        – sha256
        Feb 19 '14 at 13:22





        although works fine locally, breaks in GAE. both using 2.7

        – sha256
        Feb 19 '14 at 13:22













        @sha256 Can you show a traceback of how it breaks?

        – Matthew Schinckel
        Feb 12 '16 at 1:09





        @sha256 Can you show a traceback of how it breaks?

        – Matthew Schinckel
        Feb 12 '16 at 1:09




        3




        3





        Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

        – Antti Haapala
        Jan 15 '17 at 21:52





        Unfortunately this doesn't work in recent Python 3's. There is now some fast-path code that considers all float-subclasses as floats, and doesn't call repr on them altogether.

        – Antti Haapala
        Jan 15 '17 at 21:52













        Hilarious magic, worked with 2.7.11 on Win x64 :)

        – mlvljr
        Feb 22 '17 at 22:38





        Hilarious magic, worked with 2.7.11 on Win x64 :)

        – mlvljr
        Feb 22 '17 at 22:38











        23














        In my Flask app, Which uses python 2.7.11, flask alchemy(with 'db.decimal' types), and Flask Marshmallow ( for 'instant' serializer and deserializer), i had this error, every time i did a GET or POST. The serializer and deserializer, failed to convert Decimal types into any JSON identifiable format.



        I did a "pip install simplejson", then
        Just by adding



        import simplejson as json


        the serializer and deserializer starts to purr again. I did nothing else...
        DEciamls are displayed as '234.00' float format.






        share|improve this answer
























        • the easiest fix

          – SMDC
          May 30 '18 at 2:08
















        23














        In my Flask app, Which uses python 2.7.11, flask alchemy(with 'db.decimal' types), and Flask Marshmallow ( for 'instant' serializer and deserializer), i had this error, every time i did a GET or POST. The serializer and deserializer, failed to convert Decimal types into any JSON identifiable format.



        I did a "pip install simplejson", then
        Just by adding



        import simplejson as json


        the serializer and deserializer starts to purr again. I did nothing else...
        DEciamls are displayed as '234.00' float format.






        share|improve this answer
























        • the easiest fix

          – SMDC
          May 30 '18 at 2:08














        23












        23








        23







        In my Flask app, Which uses python 2.7.11, flask alchemy(with 'db.decimal' types), and Flask Marshmallow ( for 'instant' serializer and deserializer), i had this error, every time i did a GET or POST. The serializer and deserializer, failed to convert Decimal types into any JSON identifiable format.



        I did a "pip install simplejson", then
        Just by adding



        import simplejson as json


        the serializer and deserializer starts to purr again. I did nothing else...
        DEciamls are displayed as '234.00' float format.






        share|improve this answer













        In my Flask app, Which uses python 2.7.11, flask alchemy(with 'db.decimal' types), and Flask Marshmallow ( for 'instant' serializer and deserializer), i had this error, every time i did a GET or POST. The serializer and deserializer, failed to convert Decimal types into any JSON identifiable format.



        I did a "pip install simplejson", then
        Just by adding



        import simplejson as json


        the serializer and deserializer starts to purr again. I did nothing else...
        DEciamls are displayed as '234.00' float format.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Aug 31 '16 at 19:21









        ISONecroMAnISONecroMAn

        612618




        612618













        • the easiest fix

          – SMDC
          May 30 '18 at 2:08



















        • the easiest fix

          – SMDC
          May 30 '18 at 2:08

















        the easiest fix

        – SMDC
        May 30 '18 at 2:08





        the easiest fix

        – SMDC
        May 30 '18 at 2:08











        12














        3.9 can not be exactly represented in IEEE floats, it will always come as 3.8999999999999999, e.g. try print repr(3.9), you can read more about it here:



        http://en.wikipedia.org/wiki/Floating_point
        http://docs.sun.com/source/806-3568/ncg_goldberg.html



        So if you don't want float, only option you have to send it as string, and to allow automatic conversion of decimal objects to JSON, do something like this:



        import decimal
        from django.utils import simplejson

        def json_encode_decimal(obj):
        if isinstance(obj, decimal.Decimal):
        return str(obj)
        raise TypeError(repr(obj) + " is not JSON serializable")

        d = decimal.Decimal('3.5')
        print simplejson.dumps([d], default=json_encode_decimal)





        share|improve this answer


























        • I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

          – Knio
          Dec 25 '09 at 5:32











        • @Anurag You meant repr(obj) instead of repr(o) in your example.

          – orokusaki
          Feb 15 '10 at 0:09











        • @orokusaki, yes thanks.

          – Anurag Uniyal
          Feb 15 '10 at 4:49






        • 1





          @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

          – Anurag Uniyal
          Aug 5 '11 at 14:24






        • 1





          See mikez302's answer - in Python 2.7 or above, this no longer applies.

          – Joel Cross
          Oct 28 '13 at 15:42
















        12














        3.9 can not be exactly represented in IEEE floats, it will always come as 3.8999999999999999, e.g. try print repr(3.9), you can read more about it here:



        http://en.wikipedia.org/wiki/Floating_point
        http://docs.sun.com/source/806-3568/ncg_goldberg.html



        So if you don't want float, only option you have to send it as string, and to allow automatic conversion of decimal objects to JSON, do something like this:



        import decimal
        from django.utils import simplejson

        def json_encode_decimal(obj):
        if isinstance(obj, decimal.Decimal):
        return str(obj)
        raise TypeError(repr(obj) + " is not JSON serializable")

        d = decimal.Decimal('3.5')
        print simplejson.dumps([d], default=json_encode_decimal)





        share|improve this answer


























        • I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

          – Knio
          Dec 25 '09 at 5:32











        • @Anurag You meant repr(obj) instead of repr(o) in your example.

          – orokusaki
          Feb 15 '10 at 0:09











        • @orokusaki, yes thanks.

          – Anurag Uniyal
          Feb 15 '10 at 4:49






        • 1





          @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

          – Anurag Uniyal
          Aug 5 '11 at 14:24






        • 1





          See mikez302's answer - in Python 2.7 or above, this no longer applies.

          – Joel Cross
          Oct 28 '13 at 15:42














        12












        12








        12







        3.9 can not be exactly represented in IEEE floats, it will always come as 3.8999999999999999, e.g. try print repr(3.9), you can read more about it here:



        http://en.wikipedia.org/wiki/Floating_point
        http://docs.sun.com/source/806-3568/ncg_goldberg.html



        So if you don't want float, only option you have to send it as string, and to allow automatic conversion of decimal objects to JSON, do something like this:



        import decimal
        from django.utils import simplejson

        def json_encode_decimal(obj):
        if isinstance(obj, decimal.Decimal):
        return str(obj)
        raise TypeError(repr(obj) + " is not JSON serializable")

        d = decimal.Decimal('3.5')
        print simplejson.dumps([d], default=json_encode_decimal)





        share|improve this answer















        3.9 can not be exactly represented in IEEE floats, it will always come as 3.8999999999999999, e.g. try print repr(3.9), you can read more about it here:



        http://en.wikipedia.org/wiki/Floating_point
        http://docs.sun.com/source/806-3568/ncg_goldberg.html



        So if you don't want float, only option you have to send it as string, and to allow automatic conversion of decimal objects to JSON, do something like this:



        import decimal
        from django.utils import simplejson

        def json_encode_decimal(obj):
        if isinstance(obj, decimal.Decimal):
        return str(obj)
        raise TypeError(repr(obj) + " is not JSON serializable")

        d = decimal.Decimal('3.5')
        print simplejson.dumps([d], default=json_encode_decimal)






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Aug 28 '14 at 16:39

























        answered Dec 25 '09 at 5:29









        Anurag UniyalAnurag Uniyal

        56k29148198




        56k29148198













        • I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

          – Knio
          Dec 25 '09 at 5:32











        • @Anurag You meant repr(obj) instead of repr(o) in your example.

          – orokusaki
          Feb 15 '10 at 0:09











        • @orokusaki, yes thanks.

          – Anurag Uniyal
          Feb 15 '10 at 4:49






        • 1





          @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

          – Anurag Uniyal
          Aug 5 '11 at 14:24






        • 1





          See mikez302's answer - in Python 2.7 or above, this no longer applies.

          – Joel Cross
          Oct 28 '13 at 15:42



















        • I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

          – Knio
          Dec 25 '09 at 5:32











        • @Anurag You meant repr(obj) instead of repr(o) in your example.

          – orokusaki
          Feb 15 '10 at 0:09











        • @orokusaki, yes thanks.

          – Anurag Uniyal
          Feb 15 '10 at 4:49






        • 1





          @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

          – Anurag Uniyal
          Aug 5 '11 at 14:24






        • 1





          See mikez302's answer - in Python 2.7 or above, this no longer applies.

          – Joel Cross
          Oct 28 '13 at 15:42

















        I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

        – Knio
        Dec 25 '09 at 5:32





        I know it won't be 3.9 internally once it is parsed on the client, but 3.9 is a valid JSON float. ie, json.loads("3.9") will work, and I would like it to be this

        – Knio
        Dec 25 '09 at 5:32













        @Anurag You meant repr(obj) instead of repr(o) in your example.

        – orokusaki
        Feb 15 '10 at 0:09





        @Anurag You meant repr(obj) instead of repr(o) in your example.

        – orokusaki
        Feb 15 '10 at 0:09













        @orokusaki, yes thanks.

        – Anurag Uniyal
        Feb 15 '10 at 4:49





        @orokusaki, yes thanks.

        – Anurag Uniyal
        Feb 15 '10 at 4:49




        1




        1





        @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

        – Anurag Uniyal
        Aug 5 '11 at 14:24





        @nailer, no it won't , you can try that, reason being default raise exception to signal that next handler should be used

        – Anurag Uniyal
        Aug 5 '11 at 14:24




        1




        1





        See mikez302's answer - in Python 2.7 or above, this no longer applies.

        – Joel Cross
        Oct 28 '13 at 15:42





        See mikez302's answer - in Python 2.7 or above, this no longer applies.

        – Joel Cross
        Oct 28 '13 at 15:42











        10














        The native option is missing so I'll add it for the next guy/gall that looks for it.



        Starting on Django 1.7.x there is a built-in DjangoJSONEncoder that you can get it from django.core.serializers.json.



        import json
        from django.core.serializers.json import DjangoJSONEncoder
        from django.forms.models import model_to_dict

        model_instance = YourModel.object.first()
        model_dict = model_to_dict(model_instance)

        json.dumps(model_dict, cls=DjangoJSONEncoder)


        Presto!






        share|improve this answer
























        • Although this is great to know, the OP didn't ask about Django?

          – std''OrgnlDave
          Jul 25 '18 at 14:11






        • 1





          @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

          – Javier Buzzi
          Jul 25 '18 at 15:55






        • 1





          always good to prep for Googlers

          – std''OrgnlDave
          Jul 26 '18 at 20:07






        • 1





          you save my day

          – gaozhidf
          Feb 19 at 1:36
















        10














        The native option is missing so I'll add it for the next guy/gall that looks for it.



        Starting on Django 1.7.x there is a built-in DjangoJSONEncoder that you can get it from django.core.serializers.json.



        import json
        from django.core.serializers.json import DjangoJSONEncoder
        from django.forms.models import model_to_dict

        model_instance = YourModel.object.first()
        model_dict = model_to_dict(model_instance)

        json.dumps(model_dict, cls=DjangoJSONEncoder)


        Presto!






        share|improve this answer
























        • Although this is great to know, the OP didn't ask about Django?

          – std''OrgnlDave
          Jul 25 '18 at 14:11






        • 1





          @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

          – Javier Buzzi
          Jul 25 '18 at 15:55






        • 1





          always good to prep for Googlers

          – std''OrgnlDave
          Jul 26 '18 at 20:07






        • 1





          you save my day

          – gaozhidf
          Feb 19 at 1:36














        10












        10








        10







        The native option is missing so I'll add it for the next guy/gall that looks for it.



        Starting on Django 1.7.x there is a built-in DjangoJSONEncoder that you can get it from django.core.serializers.json.



        import json
        from django.core.serializers.json import DjangoJSONEncoder
        from django.forms.models import model_to_dict

        model_instance = YourModel.object.first()
        model_dict = model_to_dict(model_instance)

        json.dumps(model_dict, cls=DjangoJSONEncoder)


        Presto!






        share|improve this answer













        The native option is missing so I'll add it for the next guy/gall that looks for it.



        Starting on Django 1.7.x there is a built-in DjangoJSONEncoder that you can get it from django.core.serializers.json.



        import json
        from django.core.serializers.json import DjangoJSONEncoder
        from django.forms.models import model_to_dict

        model_instance = YourModel.object.first()
        model_dict = model_to_dict(model_instance)

        json.dumps(model_dict, cls=DjangoJSONEncoder)


        Presto!







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered May 22 '18 at 9:09









        Javier BuzziJavier Buzzi

        2,3081627




        2,3081627













        • Although this is great to know, the OP didn't ask about Django?

          – std''OrgnlDave
          Jul 25 '18 at 14:11






        • 1





          @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

          – Javier Buzzi
          Jul 25 '18 at 15:55






        • 1





          always good to prep for Googlers

          – std''OrgnlDave
          Jul 26 '18 at 20:07






        • 1





          you save my day

          – gaozhidf
          Feb 19 at 1:36



















        • Although this is great to know, the OP didn't ask about Django?

          – std''OrgnlDave
          Jul 25 '18 at 14:11






        • 1





          @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

          – Javier Buzzi
          Jul 25 '18 at 15:55






        • 1





          always good to prep for Googlers

          – std''OrgnlDave
          Jul 26 '18 at 20:07






        • 1





          you save my day

          – gaozhidf
          Feb 19 at 1:36

















        Although this is great to know, the OP didn't ask about Django?

        – std''OrgnlDave
        Jul 25 '18 at 14:11





        Although this is great to know, the OP didn't ask about Django?

        – std''OrgnlDave
        Jul 25 '18 at 14:11




        1




        1





        @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

        – Javier Buzzi
        Jul 25 '18 at 15:55





        @std''OrgnlDave you are 100% correct. I forgot how i got here, but i googled this question with "django" attached to the search term and this came up, after a little more googling, i found the answer and added it here for the next person like me, that stumbles across it

        – Javier Buzzi
        Jul 25 '18 at 15:55




        1




        1





        always good to prep for Googlers

        – std''OrgnlDave
        Jul 26 '18 at 20:07





        always good to prep for Googlers

        – std''OrgnlDave
        Jul 26 '18 at 20:07




        1




        1





        you save my day

        – gaozhidf
        Feb 19 at 1:36





        you save my day

        – gaozhidf
        Feb 19 at 1:36











        9














        My $.02!



        I extend a bunch of the JSON encoder since I am serializing tons of data for my web server. Here's some nice code. Note that it's easily extendable to pretty much any data format you feel like and will reproduce 3.9 as "thing": 3.9



        JSONEncoder_olddefault = json.JSONEncoder.default
        def JSONEncoder_newdefault(self, o):
        if isinstance(o, UUID): return str(o)
        if isinstance(o, datetime): return str(o)
        if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
        if isinstance(o, decimal.Decimal): return str(o)
        return JSONEncoder_olddefault(self, o)
        json.JSONEncoder.default = JSONEncoder_newdefault


        Makes my life so much easier...






        share|improve this answer



















        • 3





          This is incorrect: it will reproduce 3.9 as "thing": "3.9".

          – Glyph
          Dec 13 '17 at 22:24











        • the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

          – stackdave
          Jan 17 '18 at 15:30













        • @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

          – std''OrgnlDave
          Jan 19 '18 at 19:02






        • 2





          do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

          – Glyph
          Jan 20 '18 at 23:29
















        9














        My $.02!



        I extend a bunch of the JSON encoder since I am serializing tons of data for my web server. Here's some nice code. Note that it's easily extendable to pretty much any data format you feel like and will reproduce 3.9 as "thing": 3.9



        JSONEncoder_olddefault = json.JSONEncoder.default
        def JSONEncoder_newdefault(self, o):
        if isinstance(o, UUID): return str(o)
        if isinstance(o, datetime): return str(o)
        if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
        if isinstance(o, decimal.Decimal): return str(o)
        return JSONEncoder_olddefault(self, o)
        json.JSONEncoder.default = JSONEncoder_newdefault


        Makes my life so much easier...






        share|improve this answer



















        • 3





          This is incorrect: it will reproduce 3.9 as "thing": "3.9".

          – Glyph
          Dec 13 '17 at 22:24











        • the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

          – stackdave
          Jan 17 '18 at 15:30













        • @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

          – std''OrgnlDave
          Jan 19 '18 at 19:02






        • 2





          do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

          – Glyph
          Jan 20 '18 at 23:29














        9












        9








        9







        My $.02!



        I extend a bunch of the JSON encoder since I am serializing tons of data for my web server. Here's some nice code. Note that it's easily extendable to pretty much any data format you feel like and will reproduce 3.9 as "thing": 3.9



        JSONEncoder_olddefault = json.JSONEncoder.default
        def JSONEncoder_newdefault(self, o):
        if isinstance(o, UUID): return str(o)
        if isinstance(o, datetime): return str(o)
        if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
        if isinstance(o, decimal.Decimal): return str(o)
        return JSONEncoder_olddefault(self, o)
        json.JSONEncoder.default = JSONEncoder_newdefault


        Makes my life so much easier...






        share|improve this answer













        My $.02!



        I extend a bunch of the JSON encoder since I am serializing tons of data for my web server. Here's some nice code. Note that it's easily extendable to pretty much any data format you feel like and will reproduce 3.9 as "thing": 3.9



        JSONEncoder_olddefault = json.JSONEncoder.default
        def JSONEncoder_newdefault(self, o):
        if isinstance(o, UUID): return str(o)
        if isinstance(o, datetime): return str(o)
        if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
        if isinstance(o, decimal.Decimal): return str(o)
        return JSONEncoder_olddefault(self, o)
        json.JSONEncoder.default = JSONEncoder_newdefault


        Makes my life so much easier...







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Mar 27 '17 at 5:35









        std''OrgnlDavestd''OrgnlDave

        3,3281426




        3,3281426








        • 3





          This is incorrect: it will reproduce 3.9 as "thing": "3.9".

          – Glyph
          Dec 13 '17 at 22:24











        • the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

          – stackdave
          Jan 17 '18 at 15:30













        • @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

          – std''OrgnlDave
          Jan 19 '18 at 19:02






        • 2





          do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

          – Glyph
          Jan 20 '18 at 23:29














        • 3





          This is incorrect: it will reproduce 3.9 as "thing": "3.9".

          – Glyph
          Dec 13 '17 at 22:24











        • the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

          – stackdave
          Jan 17 '18 at 15:30













        • @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

          – std''OrgnlDave
          Jan 19 '18 at 19:02






        • 2





          do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

          – Glyph
          Jan 20 '18 at 23:29








        3




        3





        This is incorrect: it will reproduce 3.9 as "thing": "3.9".

        – Glyph
        Dec 13 '17 at 22:24





        This is incorrect: it will reproduce 3.9 as "thing": "3.9".

        – Glyph
        Dec 13 '17 at 22:24













        the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

        – stackdave
        Jan 17 '18 at 15:30







        the best solutions of all, very simple , thanks you saved my day, for me is enough to save the number, in string for decimal is ok

        – stackdave
        Jan 17 '18 at 15:30















        @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

        – std''OrgnlDave
        Jan 19 '18 at 19:02





        @Glyph via JSON standards (of which there are a few...), an unquoted number is a double-precision floating-point, not a decimal number. Quoting it is the only way to guarantee compatibility.

        – std''OrgnlDave
        Jan 19 '18 at 19:02




        2




        2





        do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

        – Glyph
        Jan 20 '18 at 23:29





        do you have a citation for this? Every spec I’ve read implies that it’s implementation-dependent.

        – Glyph
        Jan 20 '18 at 23:29











        6














        This is what I have, extracted from our class



        class CommonJSONEncoder(json.JSONEncoder):

        """
        Common JSON Encoder
        json.dumps(myString, cls=CommonJSONEncoder)
        """

        def default(self, obj):

        if isinstance(obj, decimal.Decimal):
        return {'type{decimal}': str(obj)}

        class CommonJSONDecoder(json.JSONDecoder):

        """
        Common JSON Encoder
        json.loads(myString, cls=CommonJSONEncoder)
        """

        @classmethod
        def object_hook(cls, obj):
        for key in obj:
        if isinstance(key, six.string_types):
        if 'type{decimal}' == key:
        try:
        return decimal.Decimal(obj[key])
        except:
        pass

        def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super(CommonJSONDecoder, self).__init__(**kwargs)


        Which passes unittest:



        def test_encode_and_decode_decimal(self):
        obj = Decimal('1.11')
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': Decimal('1.11')}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': {'abc': Decimal('1.11')}}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)





        share|improve this answer
























        • json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

          – Can Kavaklıoğlu
          Dec 6 '15 at 14:54













        • object_hook needs a default return value if obj is not decimal.

          – Can Kavaklıoğlu
          Feb 1 '16 at 13:14













        • What is "six.string_types" in your decoder?

          – warrens
          Dec 12 '18 at 20:42











        • @warrens pypi.org/project/six

          – James Lin
          Dec 12 '18 at 20:48
















        6














        This is what I have, extracted from our class



        class CommonJSONEncoder(json.JSONEncoder):

        """
        Common JSON Encoder
        json.dumps(myString, cls=CommonJSONEncoder)
        """

        def default(self, obj):

        if isinstance(obj, decimal.Decimal):
        return {'type{decimal}': str(obj)}

        class CommonJSONDecoder(json.JSONDecoder):

        """
        Common JSON Encoder
        json.loads(myString, cls=CommonJSONEncoder)
        """

        @classmethod
        def object_hook(cls, obj):
        for key in obj:
        if isinstance(key, six.string_types):
        if 'type{decimal}' == key:
        try:
        return decimal.Decimal(obj[key])
        except:
        pass

        def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super(CommonJSONDecoder, self).__init__(**kwargs)


        Which passes unittest:



        def test_encode_and_decode_decimal(self):
        obj = Decimal('1.11')
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': Decimal('1.11')}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': {'abc': Decimal('1.11')}}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)





        share|improve this answer
























        • json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

          – Can Kavaklıoğlu
          Dec 6 '15 at 14:54













        • object_hook needs a default return value if obj is not decimal.

          – Can Kavaklıoğlu
          Feb 1 '16 at 13:14













        • What is "six.string_types" in your decoder?

          – warrens
          Dec 12 '18 at 20:42











        • @warrens pypi.org/project/six

          – James Lin
          Dec 12 '18 at 20:48














        6












        6








        6







        This is what I have, extracted from our class



        class CommonJSONEncoder(json.JSONEncoder):

        """
        Common JSON Encoder
        json.dumps(myString, cls=CommonJSONEncoder)
        """

        def default(self, obj):

        if isinstance(obj, decimal.Decimal):
        return {'type{decimal}': str(obj)}

        class CommonJSONDecoder(json.JSONDecoder):

        """
        Common JSON Encoder
        json.loads(myString, cls=CommonJSONEncoder)
        """

        @classmethod
        def object_hook(cls, obj):
        for key in obj:
        if isinstance(key, six.string_types):
        if 'type{decimal}' == key:
        try:
        return decimal.Decimal(obj[key])
        except:
        pass

        def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super(CommonJSONDecoder, self).__init__(**kwargs)


        Which passes unittest:



        def test_encode_and_decode_decimal(self):
        obj = Decimal('1.11')
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': Decimal('1.11')}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': {'abc': Decimal('1.11')}}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)





        share|improve this answer













        This is what I have, extracted from our class



        class CommonJSONEncoder(json.JSONEncoder):

        """
        Common JSON Encoder
        json.dumps(myString, cls=CommonJSONEncoder)
        """

        def default(self, obj):

        if isinstance(obj, decimal.Decimal):
        return {'type{decimal}': str(obj)}

        class CommonJSONDecoder(json.JSONDecoder):

        """
        Common JSON Encoder
        json.loads(myString, cls=CommonJSONEncoder)
        """

        @classmethod
        def object_hook(cls, obj):
        for key in obj:
        if isinstance(key, six.string_types):
        if 'type{decimal}' == key:
        try:
        return decimal.Decimal(obj[key])
        except:
        pass

        def __init__(self, **kwargs):
        kwargs['object_hook'] = self.object_hook
        super(CommonJSONDecoder, self).__init__(**kwargs)


        Which passes unittest:



        def test_encode_and_decode_decimal(self):
        obj = Decimal('1.11')
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': Decimal('1.11')}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)

        obj = {'test': {'abc': Decimal('1.11')}}
        result = json.dumps(obj, cls=CommonJSONEncoder)
        self.assertTrue('type{decimal}' in result)
        new_obj = json.loads(result, cls=CommonJSONDecoder)
        self.assertEqual(new_obj, obj)






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Feb 2 '15 at 1:30









        James LinJames Lin

        9,1711671141




        9,1711671141













        • json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

          – Can Kavaklıoğlu
          Dec 6 '15 at 14:54













        • object_hook needs a default return value if obj is not decimal.

          – Can Kavaklıoğlu
          Feb 1 '16 at 13:14













        • What is "six.string_types" in your decoder?

          – warrens
          Dec 12 '18 at 20:42











        • @warrens pypi.org/project/six

          – James Lin
          Dec 12 '18 at 20:48



















        • json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

          – Can Kavaklıoğlu
          Dec 6 '15 at 14:54













        • object_hook needs a default return value if obj is not decimal.

          – Can Kavaklıoğlu
          Feb 1 '16 at 13:14













        • What is "six.string_types" in your decoder?

          – warrens
          Dec 12 '18 at 20:42











        • @warrens pypi.org/project/six

          – James Lin
          Dec 12 '18 at 20:48

















        json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

        – Can Kavaklıoğlu
        Dec 6 '15 at 14:54







        json.loads(myString, cls=CommonJSONEncoder) comment should be json.loads(myString, cls=CommonJSONDecoder)

        – Can Kavaklıoğlu
        Dec 6 '15 at 14:54















        object_hook needs a default return value if obj is not decimal.

        – Can Kavaklıoğlu
        Feb 1 '16 at 13:14







        object_hook needs a default return value if obj is not decimal.

        – Can Kavaklıoğlu
        Feb 1 '16 at 13:14















        What is "six.string_types" in your decoder?

        – warrens
        Dec 12 '18 at 20:42





        What is "six.string_types" in your decoder?

        – warrens
        Dec 12 '18 at 20:42













        @warrens pypi.org/project/six

        – James Lin
        Dec 12 '18 at 20:48





        @warrens pypi.org/project/six

        – James Lin
        Dec 12 '18 at 20:48











        2














        Based on stdOrgnlDave answer I have defined this wrapper that it can be called with optional kinds so the encoder will work only for certain kinds inside your projects. I believe the work should be done inside your code and not to use this "default" encoder since "it is better explicit than implicit", but I understand using this will save some of your time. :-)



        import time
        import json
        import decimal
        from uuid import UUID
        from datetime import datetime

        def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
        '''
        JSON Encoder newdfeault is a wrapper capable of encoding several kinds
        Use it anywhere on your code to make the full system to work with this defaults:
        JSONEncoder_newdefault() # for everything
        JSONEncoder_newdefault(['decimal']) # only for Decimal
        '''
        JSONEncoder_olddefault = json.JSONEncoder.default

        def JSONEncoder_wrapped(self, o):
        '''
        json.JSONEncoder.default = JSONEncoder_newdefault
        '''
        if ('uuid' in kind) and isinstance(o, uuid.UUID):
        return str(o)
        if ('datetime' in kind) and isinstance(o, datetime):
        return str(o)
        if ('time' in kind) and isinstance(o, time.struct_time):
        return datetime.fromtimestamp(time.mktime(o))
        if ('decimal' in kind) and isinstance(o, decimal.Decimal):
        return str(o)
        return JSONEncoder_olddefault(self, o)
        json.JSONEncoder.default = JSONEncoder_wrapped

        # Example
        if __name__ == '__main__':
        JSONEncoder_newdefault()





        share|improve this answer




























          2














          Based on stdOrgnlDave answer I have defined this wrapper that it can be called with optional kinds so the encoder will work only for certain kinds inside your projects. I believe the work should be done inside your code and not to use this "default" encoder since "it is better explicit than implicit", but I understand using this will save some of your time. :-)



          import time
          import json
          import decimal
          from uuid import UUID
          from datetime import datetime

          def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
          '''
          JSON Encoder newdfeault is a wrapper capable of encoding several kinds
          Use it anywhere on your code to make the full system to work with this defaults:
          JSONEncoder_newdefault() # for everything
          JSONEncoder_newdefault(['decimal']) # only for Decimal
          '''
          JSONEncoder_olddefault = json.JSONEncoder.default

          def JSONEncoder_wrapped(self, o):
          '''
          json.JSONEncoder.default = JSONEncoder_newdefault
          '''
          if ('uuid' in kind) and isinstance(o, uuid.UUID):
          return str(o)
          if ('datetime' in kind) and isinstance(o, datetime):
          return str(o)
          if ('time' in kind) and isinstance(o, time.struct_time):
          return datetime.fromtimestamp(time.mktime(o))
          if ('decimal' in kind) and isinstance(o, decimal.Decimal):
          return str(o)
          return JSONEncoder_olddefault(self, o)
          json.JSONEncoder.default = JSONEncoder_wrapped

          # Example
          if __name__ == '__main__':
          JSONEncoder_newdefault()





          share|improve this answer


























            2












            2








            2







            Based on stdOrgnlDave answer I have defined this wrapper that it can be called with optional kinds so the encoder will work only for certain kinds inside your projects. I believe the work should be done inside your code and not to use this "default" encoder since "it is better explicit than implicit", but I understand using this will save some of your time. :-)



            import time
            import json
            import decimal
            from uuid import UUID
            from datetime import datetime

            def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
            '''
            JSON Encoder newdfeault is a wrapper capable of encoding several kinds
            Use it anywhere on your code to make the full system to work with this defaults:
            JSONEncoder_newdefault() # for everything
            JSONEncoder_newdefault(['decimal']) # only for Decimal
            '''
            JSONEncoder_olddefault = json.JSONEncoder.default

            def JSONEncoder_wrapped(self, o):
            '''
            json.JSONEncoder.default = JSONEncoder_newdefault
            '''
            if ('uuid' in kind) and isinstance(o, uuid.UUID):
            return str(o)
            if ('datetime' in kind) and isinstance(o, datetime):
            return str(o)
            if ('time' in kind) and isinstance(o, time.struct_time):
            return datetime.fromtimestamp(time.mktime(o))
            if ('decimal' in kind) and isinstance(o, decimal.Decimal):
            return str(o)
            return JSONEncoder_olddefault(self, o)
            json.JSONEncoder.default = JSONEncoder_wrapped

            # Example
            if __name__ == '__main__':
            JSONEncoder_newdefault()





            share|improve this answer













            Based on stdOrgnlDave answer I have defined this wrapper that it can be called with optional kinds so the encoder will work only for certain kinds inside your projects. I believe the work should be done inside your code and not to use this "default" encoder since "it is better explicit than implicit", but I understand using this will save some of your time. :-)



            import time
            import json
            import decimal
            from uuid import UUID
            from datetime import datetime

            def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
            '''
            JSON Encoder newdfeault is a wrapper capable of encoding several kinds
            Use it anywhere on your code to make the full system to work with this defaults:
            JSONEncoder_newdefault() # for everything
            JSONEncoder_newdefault(['decimal']) # only for Decimal
            '''
            JSONEncoder_olddefault = json.JSONEncoder.default

            def JSONEncoder_wrapped(self, o):
            '''
            json.JSONEncoder.default = JSONEncoder_newdefault
            '''
            if ('uuid' in kind) and isinstance(o, uuid.UUID):
            return str(o)
            if ('datetime' in kind) and isinstance(o, datetime):
            return str(o)
            if ('time' in kind) and isinstance(o, time.struct_time):
            return datetime.fromtimestamp(time.mktime(o))
            if ('decimal' in kind) and isinstance(o, decimal.Decimal):
            return str(o)
            return JSONEncoder_olddefault(self, o)
            json.JSONEncoder.default = JSONEncoder_wrapped

            # Example
            if __name__ == '__main__':
            JSONEncoder_newdefault()






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Jan 17 '18 at 11:12









            Juanmi TaboadaJuanmi Taboada

            385516




            385516























                2














                From the JSON Standard Document, as linked in json.org:




                JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of
                number types of various capacities and complements, fixed or floating, binary or decimal. That can make
                interchange between different programming languages difficult. JSON instead offers only the representation of
                numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
                sequences even if they disagree on internal representations. That is enough to allow interchange.




                So it's actually accurate to represent Decimals as numbers (rather than strings) in JSON. Bellow lies a possible solution to the problem.



                Define a custom JSON encoder:



                import json


                class CustomJsonEncoder(json.JSONEncoder):

                def default(self, obj):
                if isinstance(obj, Decimal):
                return float(obj)
                return super(CustomJsonEncoder, self).default(obj)


                Then use it when serializing your data:



                json.dumps(data, cls=CustomJsonEncoder)


                As noted from comments on the other answers, older versions of python might mess up the representation when converting to float, but that's not the case anymore.



                To get the decimal back in Python:



                Decimal(str(value))


                This solution is hinted in Python 3.0 documentation on decimals:




                To create a Decimal from a float, first convert it to a string.







                share|improve this answer





















                • 1





                  This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                  – juanpa.arrivillaga
                  Jan 1 at 19:58













                • I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                  – hugo_leonardo
                  Jan 26 at 19:51











                • With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                  – hugo_leonardo
                  Jan 26 at 19:53
















                2














                From the JSON Standard Document, as linked in json.org:




                JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of
                number types of various capacities and complements, fixed or floating, binary or decimal. That can make
                interchange between different programming languages difficult. JSON instead offers only the representation of
                numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
                sequences even if they disagree on internal representations. That is enough to allow interchange.




                So it's actually accurate to represent Decimals as numbers (rather than strings) in JSON. Bellow lies a possible solution to the problem.



                Define a custom JSON encoder:



                import json


                class CustomJsonEncoder(json.JSONEncoder):

                def default(self, obj):
                if isinstance(obj, Decimal):
                return float(obj)
                return super(CustomJsonEncoder, self).default(obj)


                Then use it when serializing your data:



                json.dumps(data, cls=CustomJsonEncoder)


                As noted from comments on the other answers, older versions of python might mess up the representation when converting to float, but that's not the case anymore.



                To get the decimal back in Python:



                Decimal(str(value))


                This solution is hinted in Python 3.0 documentation on decimals:




                To create a Decimal from a float, first convert it to a string.







                share|improve this answer





















                • 1





                  This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                  – juanpa.arrivillaga
                  Jan 1 at 19:58













                • I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                  – hugo_leonardo
                  Jan 26 at 19:51











                • With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                  – hugo_leonardo
                  Jan 26 at 19:53














                2












                2








                2







                From the JSON Standard Document, as linked in json.org:




                JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of
                number types of various capacities and complements, fixed or floating, binary or decimal. That can make
                interchange between different programming languages difficult. JSON instead offers only the representation of
                numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
                sequences even if they disagree on internal representations. That is enough to allow interchange.




                So it's actually accurate to represent Decimals as numbers (rather than strings) in JSON. Bellow lies a possible solution to the problem.



                Define a custom JSON encoder:



                import json


                class CustomJsonEncoder(json.JSONEncoder):

                def default(self, obj):
                if isinstance(obj, Decimal):
                return float(obj)
                return super(CustomJsonEncoder, self).default(obj)


                Then use it when serializing your data:



                json.dumps(data, cls=CustomJsonEncoder)


                As noted from comments on the other answers, older versions of python might mess up the representation when converting to float, but that's not the case anymore.



                To get the decimal back in Python:



                Decimal(str(value))


                This solution is hinted in Python 3.0 documentation on decimals:




                To create a Decimal from a float, first convert it to a string.







                share|improve this answer















                From the JSON Standard Document, as linked in json.org:




                JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of
                number types of various capacities and complements, fixed or floating, binary or decimal. That can make
                interchange between different programming languages difficult. JSON instead offers only the representation of
                numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
                sequences even if they disagree on internal representations. That is enough to allow interchange.




                So it's actually accurate to represent Decimals as numbers (rather than strings) in JSON. Bellow lies a possible solution to the problem.



                Define a custom JSON encoder:



                import json


                class CustomJsonEncoder(json.JSONEncoder):

                def default(self, obj):
                if isinstance(obj, Decimal):
                return float(obj)
                return super(CustomJsonEncoder, self).default(obj)


                Then use it when serializing your data:



                json.dumps(data, cls=CustomJsonEncoder)


                As noted from comments on the other answers, older versions of python might mess up the representation when converting to float, but that's not the case anymore.



                To get the decimal back in Python:



                Decimal(str(value))


                This solution is hinted in Python 3.0 documentation on decimals:




                To create a Decimal from a float, first convert it to a string.








                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Feb 8 at 16:51

























                answered Apr 8 '18 at 1:30









                hugo_leonardohugo_leonardo

                5,15572849




                5,15572849








                • 1





                  This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                  – juanpa.arrivillaga
                  Jan 1 at 19:58













                • I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                  – hugo_leonardo
                  Jan 26 at 19:51











                • With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                  – hugo_leonardo
                  Jan 26 at 19:53














                • 1





                  This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                  – juanpa.arrivillaga
                  Jan 1 at 19:58













                • I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                  – hugo_leonardo
                  Jan 26 at 19:51











                • With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                  – hugo_leonardo
                  Jan 26 at 19:53








                1




                1





                This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                – juanpa.arrivillaga
                Jan 1 at 19:58







                This isn't "fixed" in Python 3. Converting to a float necessarily makes you lose the decimal representation, and will lead to discrepancies. If Decimal is important to use, I think it is better to use strings.

                – juanpa.arrivillaga
                Jan 1 at 19:58















                I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                – hugo_leonardo
                Jan 26 at 19:51





                I believe it's safe to do this since python 3.1. The loss in precision might be harmful in arithmetic operations, but in the case of JSON encoding, you are merely producing a string display of the value, so the precision is more than enough for most use cases. Everything in JSON is a string already, so putting quotes around the value just defies the JSON spec.

                – hugo_leonardo
                Jan 26 at 19:51













                With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                – hugo_leonardo
                Jan 26 at 19:53





                With that said, I understand the concerns around converting to float. There's probably a different strategy to use with the encoder to produce the wanted display string. Still, I don't think it's worth producing a quoted value.

                – hugo_leonardo
                Jan 26 at 19:53











                0














                If you want to pass a dictionary containing decimals to the requests library (using the json keyword argument), you simply need to install simplejson:



                $ pip3 install simplejson    
                $ python3
                >>> import requests
                >>> from decimal import Decimal
                >>> # This won't error out:
                >>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})


                The reason of the problem is that requests uses simplejson only if it is present, and falls back to the built-in json if it is not installed.






                share|improve this answer




























                  0














                  If you want to pass a dictionary containing decimals to the requests library (using the json keyword argument), you simply need to install simplejson:



                  $ pip3 install simplejson    
                  $ python3
                  >>> import requests
                  >>> from decimal import Decimal
                  >>> # This won't error out:
                  >>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})


                  The reason of the problem is that requests uses simplejson only if it is present, and falls back to the built-in json if it is not installed.






                  share|improve this answer


























                    0












                    0








                    0







                    If you want to pass a dictionary containing decimals to the requests library (using the json keyword argument), you simply need to install simplejson:



                    $ pip3 install simplejson    
                    $ python3
                    >>> import requests
                    >>> from decimal import Decimal
                    >>> # This won't error out:
                    >>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})


                    The reason of the problem is that requests uses simplejson only if it is present, and falls back to the built-in json if it is not installed.






                    share|improve this answer













                    If you want to pass a dictionary containing decimals to the requests library (using the json keyword argument), you simply need to install simplejson:



                    $ pip3 install simplejson    
                    $ python3
                    >>> import requests
                    >>> from decimal import Decimal
                    >>> # This won't error out:
                    >>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})


                    The reason of the problem is that requests uses simplejson only if it is present, and falls back to the built-in json if it is not installed.







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Apr 14 '18 at 19:22









                    Max MalyshMax Malysh

                    8,22985167




                    8,22985167























                        0














                        You can create a custom JSON encoder as per your requirement.



                        import json
                        from datetime import datetime, date
                        from time import time, struct_time, mktime
                        import decimal

                        class CustomJSONEncoder(json.JSONEncoder):
                        def default(self, o):
                        if isinstance(o, datetime):
                        return str(o)
                        if isinstance(o, date):
                        return str(o)
                        if isinstance(o, decimal.Decimal):
                        return float(o)
                        if isinstance(o, struct_time):
                        return datetime.fromtimestamp(mktime(o))
                        # Any other serializer if needed
                        return super(CustomJSONEncoder, self).default(o)


                        The Decoder can be called like this,



                        import json
                        from decimal import Decimal
                        json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)


                        and the output will be:



                        >>'{"x": 3.9}'





                        share|improve this answer




























                          0














                          You can create a custom JSON encoder as per your requirement.



                          import json
                          from datetime import datetime, date
                          from time import time, struct_time, mktime
                          import decimal

                          class CustomJSONEncoder(json.JSONEncoder):
                          def default(self, o):
                          if isinstance(o, datetime):
                          return str(o)
                          if isinstance(o, date):
                          return str(o)
                          if isinstance(o, decimal.Decimal):
                          return float(o)
                          if isinstance(o, struct_time):
                          return datetime.fromtimestamp(mktime(o))
                          # Any other serializer if needed
                          return super(CustomJSONEncoder, self).default(o)


                          The Decoder can be called like this,



                          import json
                          from decimal import Decimal
                          json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)


                          and the output will be:



                          >>'{"x": 3.9}'





                          share|improve this answer


























                            0












                            0








                            0







                            You can create a custom JSON encoder as per your requirement.



                            import json
                            from datetime import datetime, date
                            from time import time, struct_time, mktime
                            import decimal

                            class CustomJSONEncoder(json.JSONEncoder):
                            def default(self, o):
                            if isinstance(o, datetime):
                            return str(o)
                            if isinstance(o, date):
                            return str(o)
                            if isinstance(o, decimal.Decimal):
                            return float(o)
                            if isinstance(o, struct_time):
                            return datetime.fromtimestamp(mktime(o))
                            # Any other serializer if needed
                            return super(CustomJSONEncoder, self).default(o)


                            The Decoder can be called like this,



                            import json
                            from decimal import Decimal
                            json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)


                            and the output will be:



                            >>'{"x": 3.9}'





                            share|improve this answer













                            You can create a custom JSON encoder as per your requirement.



                            import json
                            from datetime import datetime, date
                            from time import time, struct_time, mktime
                            import decimal

                            class CustomJSONEncoder(json.JSONEncoder):
                            def default(self, o):
                            if isinstance(o, datetime):
                            return str(o)
                            if isinstance(o, date):
                            return str(o)
                            if isinstance(o, decimal.Decimal):
                            return float(o)
                            if isinstance(o, struct_time):
                            return datetime.fromtimestamp(mktime(o))
                            # Any other serializer if needed
                            return super(CustomJSONEncoder, self).default(o)


                            The Decoder can be called like this,



                            import json
                            from decimal import Decimal
                            json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)


                            and the output will be:



                            >>'{"x": 3.9}'






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Sep 6 '18 at 11:47









                            sparrowsparrow

                            924




                            924























                                -5














                                this can be done by adding



                                    elif isinstance(o, decimal.Decimal):
                                yield str(o)


                                in Libjsonencoder.py:JSONEncoder._iterencode, but I was hoping for a better solution






                                share|improve this answer



















                                • 3





                                  You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                  – justanr
                                  Jan 17 '15 at 22:11
















                                -5














                                this can be done by adding



                                    elif isinstance(o, decimal.Decimal):
                                yield str(o)


                                in Libjsonencoder.py:JSONEncoder._iterencode, but I was hoping for a better solution






                                share|improve this answer



















                                • 3





                                  You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                  – justanr
                                  Jan 17 '15 at 22:11














                                -5












                                -5








                                -5







                                this can be done by adding



                                    elif isinstance(o, decimal.Decimal):
                                yield str(o)


                                in Libjsonencoder.py:JSONEncoder._iterencode, but I was hoping for a better solution






                                share|improve this answer













                                this can be done by adding



                                    elif isinstance(o, decimal.Decimal):
                                yield str(o)


                                in Libjsonencoder.py:JSONEncoder._iterencode, but I was hoping for a better solution







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Dec 25 '09 at 5:37









                                KnioKnio

                                2,77631922




                                2,77631922








                                • 3





                                  You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                  – justanr
                                  Jan 17 '15 at 22:11














                                • 3





                                  You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                  – justanr
                                  Jan 17 '15 at 22:11








                                3




                                3





                                You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                – justanr
                                Jan 17 '15 at 22:11





                                You can subclass JSONEncoder as exampled above, editing the installed Python files of an established library or the interpreter itself should be a very last resort.

                                – justanr
                                Jan 17 '15 at 22:11


















                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f1960516%2fpython-json-serialize-a-decimal-object%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

                                Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

                                A Topological Invariant for $pi_3(U(n))$