Python JSON serialize a Decimal object
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
add a comment |
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
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
add a comment |
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
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
python json floating-point decimal
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
add a comment |
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
add a comment |
14 Answers
14
active
oldest
votes
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)
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 thatDecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
returned the correct'3.9'
, butDecimalEncoder()._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 justreturn (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
|
show 9 more comments
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.
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 tojson.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 usinguse_decimal=True
on the loads, too.
– Matthew Schinckel
Nov 23 '11 at 23:04
3
simplejson.dumps(decimal.Decimal('2.2'))
also works: no explicituse_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 stdlibjson
(and oldsimplejson
versions are also supported).
– jfs
Jul 27 '14 at 14:04
|
show 4 more comments
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.
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
Useunicode
orstr
instead offloat
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
|
show 2 more comments
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]'
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
|
show 4 more comments
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.
the easiest fix
– SMDC
May 30 '18 at 2:08
add a comment |
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)
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
|
show 2 more comments
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!
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
add a comment |
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...
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
add a comment |
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)
json.loads(myString, cls=CommonJSONEncoder)
comment should bejson.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
add a comment |
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()
add a comment |
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.
1
This isn't "fixed" in Python 3. Converting to afloat
necessarily makes you lose the decimal representation, and will lead to discrepancies. IfDecimal
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
add a comment |
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.
add a comment |
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}'
add a comment |
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
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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)
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 thatDecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
returned the correct'3.9'
, butDecimalEncoder()._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 justreturn (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
|
show 9 more comments
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)
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 thatDecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
returned the correct'3.9'
, butDecimalEncoder()._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 justreturn (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
|
show 9 more comments
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)
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)
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 thatDecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
returned the correct'3.9'
, butDecimalEncoder()._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 justreturn (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
|
show 9 more comments
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 thatDecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
returned the correct'3.9'
, butDecimalEncoder()._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 justreturn (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
|
show 9 more comments
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.
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 tojson.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 usinguse_decimal=True
on the loads, too.
– Matthew Schinckel
Nov 23 '11 at 23:04
3
simplejson.dumps(decimal.Decimal('2.2'))
also works: no explicituse_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 stdlibjson
(and oldsimplejson
versions are also supported).
– jfs
Jul 27 '14 at 14:04
|
show 4 more comments
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.
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 tojson.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 usinguse_decimal=True
on the loads, too.
– Matthew Schinckel
Nov 23 '11 at 23:04
3
simplejson.dumps(decimal.Decimal('2.2'))
also works: no explicituse_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 stdlibjson
(and oldsimplejson
versions are also supported).
– jfs
Jul 27 '14 at 14:04
|
show 4 more comments
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.
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.
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 tojson.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 usinguse_decimal=True
on the loads, too.
– Matthew Schinckel
Nov 23 '11 at 23:04
3
simplejson.dumps(decimal.Decimal('2.2'))
also works: no explicituse_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 stdlibjson
(and oldsimplejson
versions are also supported).
– jfs
Jul 27 '14 at 14:04
|
show 4 more comments
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 tojson.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 usinguse_decimal=True
on the loads, too.
– Matthew Schinckel
Nov 23 '11 at 23:04
3
simplejson.dumps(decimal.Decimal('2.2'))
also works: no explicituse_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 stdlibjson
(and oldsimplejson
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
|
show 4 more comments
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.
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
Useunicode
orstr
instead offloat
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
|
show 2 more comments
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.
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
Useunicode
orstr
instead offloat
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
|
show 2 more comments
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.
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.
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
Useunicode
orstr
instead offloat
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
|
show 2 more comments
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
Useunicode
orstr
instead offloat
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
|
show 2 more comments
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]'
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
|
show 4 more comments
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]'
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
|
show 4 more comments
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]'
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]'
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
|
show 4 more comments
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
|
show 4 more comments
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.
the easiest fix
– SMDC
May 30 '18 at 2:08
add a comment |
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.
the easiest fix
– SMDC
May 30 '18 at 2:08
add a comment |
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.
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.
answered Aug 31 '16 at 19:21
ISONecroMAnISONecroMAn
612618
612618
the easiest fix
– SMDC
May 30 '18 at 2:08
add a comment |
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
add a comment |
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)
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
|
show 2 more comments
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)
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
|
show 2 more comments
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)
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)
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
|
show 2 more comments
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
|
show 2 more comments
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!
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
add a comment |
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!
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
add a comment |
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!
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!
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
add a comment |
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
add a comment |
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...
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
add a comment |
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...
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
add a comment |
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...
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...
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
add a comment |
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
add a comment |
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)
json.loads(myString, cls=CommonJSONEncoder)
comment should bejson.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
add a comment |
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)
json.loads(myString, cls=CommonJSONEncoder)
comment should bejson.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
add a comment |
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)
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)
answered Feb 2 '15 at 1:30
James LinJames Lin
9,1711671141
9,1711671141
json.loads(myString, cls=CommonJSONEncoder)
comment should bejson.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
add a comment |
json.loads(myString, cls=CommonJSONEncoder)
comment should bejson.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
add a comment |
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()
add a comment |
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()
add a comment |
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()
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()
answered Jan 17 '18 at 11:12
Juanmi TaboadaJuanmi Taboada
385516
385516
add a comment |
add a comment |
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.
1
This isn't "fixed" in Python 3. Converting to afloat
necessarily makes you lose the decimal representation, and will lead to discrepancies. IfDecimal
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
add a comment |
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.
1
This isn't "fixed" in Python 3. Converting to afloat
necessarily makes you lose the decimal representation, and will lead to discrepancies. IfDecimal
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
add a comment |
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.
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.
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 afloat
necessarily makes you lose the decimal representation, and will lead to discrepancies. IfDecimal
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
add a comment |
1
This isn't "fixed" in Python 3. Converting to afloat
necessarily makes you lose the decimal representation, and will lead to discrepancies. IfDecimal
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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Apr 14 '18 at 19:22
Max MalyshMax Malysh
8,22985167
8,22985167
add a comment |
add a comment |
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}'
add a comment |
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}'
add a comment |
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}'
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}'
answered Sep 6 '18 at 11:47
sparrowsparrow
924
924
add a comment |
add a comment |
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
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
add a comment |
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
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
add a comment |
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
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
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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