Python JSON tuần tự hóa một đối tượng thập phân


242

Tôi có một Decimal('3.9')phần của một đối tượng và muốn mã hóa chuỗi này thành chuỗi JSON trông giống như vậy {'x': 3.9}. Tôi không quan tâm đến độ chính xác ở phía khách hàng, vì vậy nổi là ổn.

Có một cách tốt để nối tiếp này? JSONDecoder không chấp nhận các đối tượng Decimal và chuyển đổi thành float trước đó mang lại kết quả {'x': 3.8999999999999999}sai và sẽ gây lãng phí lớn về băng thông.



3.8999999999999999 không sai hơn 3,4 là. 0,2 không có đại diện chính xác nổi.
Jasen

@Jasen 3.89999999999 sai hơn khoảng 12,8% so với 3,4. Tiêu chuẩn JSON chỉ là về tuần tự hóa và ký hiệu, không triển khai. Sử dụng IEEE754 không phải là một phần của thông số JSON thô, đây chỉ là cách phổ biến nhất để triển khai nó. Một triển khai chỉ sử dụng số học thập phân chính xác là hoàn toàn (trên thực tế, thậm chí nghiêm ngặt hơn) tuân thủ.
hraban

😂 ít sai. mỉa mai
hraban

Câu trả lời:


147

Làm thế nào về phân lớp 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)

Sau đó sử dụng nó như vậy:

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

Ouch, tôi chỉ nhận thấy rằng nó sẽ không thực sự hoạt động như thế này. Sẽ chỉnh sửa cho phù hợp. (Tuy nhiên, ý tưởng vẫn giữ nguyên.)
Michał Marc: 05

Vấn đề là đã DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()trả về đúng '3.9', nhưng DecimalEncoder()._iterencode(3.9).next()trả về một đối tượng trình tạo sẽ chỉ trả về '3.899...'khi bạn chồng chất lên nhau .next(). Máy phát điện vui kinh doanh. Oh well ... Nên làm việc bây giờ.
Michał Marc: 05

8
Bạn có thể chỉ return (str(o),)thay thế? [o]là một danh sách chỉ có 1 phần tử, tại sao phải lặp lại nó?
mở

2
@Mark: return (str(o),)sẽ trả về tuple có độ dài 1, trong khi mã trong câu trả lời trả về trình tạo độ dài 1. Xem tài liệu iterencode ()
Abgan

30
Việc thực hiện này không còn hiệu quả nữa. Người của Elias Zamaria là người làm việc theo cùng một phong cách.
piro

223

Simplejson 2.1 trở lên có hỗ trợ riêng cho loại Thập phân:

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

Lưu ý rằng use_decimalTruetheo mặc định:

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):

Vì thế:

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

Hy vọng, tính năng này sẽ được đưa vào thư viện tiêu chuẩn.


7
Hmm, đối với tôi điều này chuyển đổi các đối tượng thập phân thành phao, điều này không được chấp nhận. Mất độ chính xác khi làm việc với tiền tệ, ví dụ.
Matthew Schinckel

12
@MatthewSchinckel Tôi nghĩ là không. Nó thực sự làm cho một chuỗi ra khỏi nó. Và nếu bạn đưa chuỗi kết quả trở lại chuỗi json.loads(s, use_decimal=True)đó sẽ trả lại cho bạn số thập phân. Không nổi trong toàn bộ quá trình. Chỉnh sửa câu trả lời trên. Hy vọng poster ban đầu là tốt với nó.
Shekhar

1
Aha, tôi nghĩ rằng tôi cũng không sử dụng use_decimal=Truetải.
Matthew Schinckel

1
Cho tôi json.dumps({'a' : Decimal('3.9')}, use_decimal=True)cho '{"a": 3.9}'. Là mục tiêu không '{"a": "3.9"}'?
MrJ

5
simplejson.dumps(decimal.Decimal('2.2'))cũng hoạt động: không rõ ràng use_decimal(được thử nghiệm trên Simplejson / 3.6.0). Một cách khác để tải lại là: json.loads(s, parse_float=Decimal)tức là bạn có thể đọc nó bằng stdlib json(và simplejsoncác phiên bản cũ cũng được hỗ trợ).
JFS

181

Tôi muốn cho mọi người biết rằng tôi đã thử câu trả lời của Michał Marchot trên máy chủ web của tôi đang chạy Python 2.6.5 và nó hoạt động tốt. Tuy nhiên, tôi đã nâng cấp lên Python 2.7 và nó đã ngừng hoạt động. Tôi đã cố gắng nghĩ ra một số cách để mã hóa các đối tượng Thập phân và đây là những gì tôi nghĩ ra:

import decimal

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

Điều này hy vọng sẽ giúp được bất cứ ai gặp vấn đề với Python 2.7. Tôi đã thử nó và nó có vẻ hoạt động tốt. Nếu bất cứ ai nhận thấy bất kỳ lỗi trong giải pháp của tôi hoặc đưa ra một cách tốt hơn, xin vui lòng cho tôi biết.


4
Python 2.7 đã thay đổi các quy tắc làm tròn số float để nó hoạt động. Xem thảo luận trong stackoverflow.com/questions/1447287/ Ấn
Nelson

2
Đối với những người trong chúng ta không thể sử dụng Simplejson (ví dụ: trên Google App Engine), câu trả lời này là một ơn trời.
Joel Cross

17
Sử dụng unicodehoặc strthay vì floatđể đảm bảo độ chính xác.
Seppo Erviälä

2
Vấn đề với 54.3999 ... rất quan trọng trong Python 2.6.x trở lên khi chuỗi chuyển đổi thành chuỗi không hoạt động thường xuyên, nhưng chuyển đổi Decimal thành str không chính xác hơn vì nó sẽ được tuần tự hóa thành chuỗi có dấu ngoặc kép "54.4", không phải là chuỗi một số.
hynekcer

1
Hoạt động trong python3
SeanFromIT

43

Trong ứng dụng Flask của tôi, sử dụng python 2.7.11, alchemy bình (với các loại 'db.decimal') và Flask Marshmallow (đối với trình tuần tự hóa và trình giải mã 'tức thời'), tôi đã gặp lỗi này, mỗi khi tôi thực hiện GET hoặc POST . Trình tuần tự hóa và trình giải tuần tự, không thể chuyển đổi các loại thập phân thành bất kỳ định dạng nhận dạng JSON nào.

Tôi đã thực hiện một "pip install simplejson", sau đó chỉ bằng cách thêm

import simplejson as json

serializer và deserializer bắt đầu rú lên một lần nữa. Tôi không làm gì khác ... DEciamls được hiển thị ở định dạng float '234.00'.


1
cách khắc phục dễ dàng nhất
SMDC

1
Thật kỳ lạ, bạn thậm chí không phải nhập simplejson- chỉ cần cài đặt nó là thủ thuật. Ban đầu được đề cập bởi câu trả lời này .
bsploding

Điều này không hoạt động với tôi, và vẫn nhận được nó Decimal('0.00') is not JSON serializable sau khi cài đặt nó qua pip. Tình huống này là khi bạn đang sử dụng cả marshmallow và graphene. Khi một truy vấn được gọi trên một api còn lại, marshmallow hoạt động dự kiến ​​cho các trường thập phân. Tuy nhiên, khi nó được gọi với graphql, nó đã phát sinh is not JSON serializablelỗi.
Roel

Tuyệt vời, Tuyệt vời,
Người nhện

Hoàn hảo! Điều này hoạt động trong các tình huống khi bạn đang sử dụng một mô-đun được viết bởi người khác, vì bạn không thể dễ dàng sửa đổi (trong trường hợp của tôi là sử dụng Google Sheets)
happyskeptic

32

Tôi đã thử chuyển từ đơn giản sang tích hợp sẵn cho GAE 2.7 và gặp vấn đề với số thập phân. Nếu mặc định trả về str (o), có dấu ngoặc kép (vì _iterencode gọi _iterencode trên kết quả mặc định) và float (o) sẽ xóa dấu 0.

Nếu mặc định trả về một đối tượng của một lớp kế thừa từ float (hoặc bất cứ thứ gì gọi repr mà không cần định dạng bổ sung) và có một phương thức __Vpr__ tùy chỉnh, nó dường như hoạt động như tôi muốn.

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]'

Đẹp! Điều này đảm bảo giá trị thập phân kết thúc trong JSON dưới dạng float Javascript, mà không cần Python làm tròn nó với giá trị float gần nhất.
konrad

3
Thật không may, điều này không hoạt động trong Python 3 gần đây. Hiện tại có một số mã đường dẫn nhanh coi tất cả các lớp con float là float và không gọi repr trên chúng hoàn toàn.
Antti Haapala

@AnttiHaapala, ví dụ hoạt động tốt trên Python 3.6.
Cristian Ciupitu

@CristianCiupitu thực sự, tôi dường như không thể tái tạo hành vi xấu bây giờ
Antti Haapala

2
Giải pháp đã ngừng hoạt động kể từ v3.5.2rc1, xem github.com/python/cpython/commit/ mẹo . Có float.__repr__mã hóa cứng (mất độ chính xác) và hoàn toàn fakefloat.__repr__không được gọi. Giải pháp trên không hoạt động đúng với python3 lên tới 3.5.1, nếu fakefloat có phương pháp bổ sung def __float__(self): return self.
myroslav

30

Tùy chọn gốc bị thiếu, vì vậy tôi sẽ thêm nó cho anh chàng / gall tiếp theo tìm kiếm nó.

Bắt đầu trên Django 1.7.x có một tích hợp DjangoJSONEncodermà bạn có thể lấy nó từ đó 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)

Mau!


Mặc dù điều này là tuyệt vời để biết, OP đã không hỏi về Django?
std''OrgnlDave

4
@ std''OrgnlDave bạn đúng 100%. Tôi đã quên làm thế nào tôi đến đây, nhưng tôi đã đặt câu hỏi này với "django" được gắn với cụm từ tìm kiếm và điều này xuất hiện, sau khi thêm một chút, tôi đã tìm thấy câu trả lời và thêm nó vào đây cho người tiếp theo như tôi, tình cờ gặp nó
Javier Buzzi

6
bạn tiết kiệm trong ngày của tôi
gaozhidf

14

$ 0,02 của tôi!

Tôi mở rộng một loạt các bộ mã hóa JSON vì tôi đang tuần tự hóa hàng tấn dữ liệu cho máy chủ web của mình. Đây là một số mã tốt đẹp. Lưu ý rằng nó có thể dễ dàng mở rộng đến hầu hết mọi định dạng dữ liệu mà bạn cảm thấy thích và sẽ sao chép 3.9 như"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

Làm cho cuộc sống của tôi dễ dàng hơn nhiều ...


3
Điều này là không chính xác: nó sẽ sao chép 3.9 như "thing": "3.9".
Glyph

Các giải pháp tốt nhất trong tất cả, rất đơn giản, cảm ơn bạn đã lưu ngày của tôi, đối với tôi là đủ để lưu số, trong chuỗi cho số thập phân là ok
stackdave

@Glyph thông qua các tiêu chuẩn JSON (trong đó có một vài ...), một số không được trích dẫn là một dấu phẩy động có độ chính xác kép, không phải là số thập phân. Trích dẫn nó là cách duy nhất để đảm bảo tính tương thích.
std''OrgnlDave

2
Bạn có một trích dẫn cho điều này? Mỗi thông số tôi đọc đều ngụ ý rằng nó phụ thuộc vào việc thực hiện.
Glyph

12

3.9không thể được trình bày chính xác trong các float của IEEE, nó sẽ luôn xuất hiện 3.8999999999999999, ví dụ như thử print repr(3.9) , bạn có thể đọc thêm về nó ở đây:

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

Vì vậy, nếu bạn không muốn float, chỉ có tùy chọn bạn phải gửi nó dưới dạng chuỗi và để cho phép tự động chuyển đổi các đối tượng thập phân thành JSON, hãy làm một cái gì đó như sau:

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)

Tôi biết nó sẽ không phải là 3.9 trong nội bộ khi nó được phân tích cú pháp trên máy khách, nhưng 3.9 là một float JSON hợp lệ. tức là, json.loads("3.9")sẽ hoạt động và tôi muốn nó là thế này
Knio

@Anurag Bạn có nghĩa là repr (obj) thay vì repr (o) trong ví dụ của bạn.
orokusaki

Điều này sẽ không chết nếu bạn thử và mã hóa thứ gì đó không phải là số thập phân?
mikemaccana

1
@nailer, không, nó sẽ không, bạn có thể thử điều đó, lý do là mặc định tăng ngoại lệ để báo hiệu rằng trình xử lý tiếp theo sẽ được sử dụng
Anurag Uniyal

1
Xem câu trả lời của mikez302 - trong Python 2.7 trở lên, điều này không còn áp dụng.
Joel Cross

9

Đối với người dùng Django :

Gần đây đã xuất hiện TypeError: Decimal('2337.00') is not JSON serializable trong khi mã hóa JSON tức làjson.dumps(data)

Giải pháp :

# converts Decimal, Datetime, UUIDs to str for Encoding
from django.core.serializers.json import DjangoJSONEncoder  

json.dumps(response.data, cls=DjangoJSONEncoder)

Nhưng, bây giờ giá trị thập phân sẽ là một chuỗi, bây giờ chúng ta có thể đặt rõ ràng trình phân tích cú pháp giá trị thập phân / dấu phẩy khi giải mã dữ liệu, sử dụng parse_floattùy chọn trong json.loads:

import decimal 

data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)

8

Từ Tài liệu chuẩn JSON , như được liên kết trong json.org :

JSON là bất khả tri về ngữ nghĩa của các con số. Trong bất kỳ ngôn ngữ lập trình nào, có thể có nhiều loại số lượng năng lực và bổ sung khác nhau, cố định hoặc trôi nổi, nhị phân hoặc thập phân. Điều đó có thể làm cho việc trao đổi giữa các ngôn ngữ lập trình khác nhau trở nên khó khăn. Thay vào đó, JSON chỉ cung cấp biểu diễn các số mà con người sử dụng: một chuỗi các chữ số. Tất cả các ngôn ngữ lập trình đều biết cách hiểu ý nghĩa của các chuỗi số ngay cả khi chúng không đồng ý với các biểu diễn bên trong. Điều đó là đủ để cho phép trao đổi.

Vì vậy, thật sự chính xác khi biểu diễn Số thập phân dưới dạng số (chứ không phải chuỗi) trong JSON. Dưới đây là một giải pháp có thể cho vấn đề.

Xác định bộ mã hóa JSON tùy chỉnh:

import json


class CustomJsonEncoder(json.JSONEncoder):

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

Sau đó sử dụng nó khi tuần tự hóa dữ liệu của bạn:

json.dumps(data, cls=CustomJsonEncoder)

Như đã lưu ý từ các nhận xét về các câu trả lời khác, các phiên bản cũ hơn của trăn có thể làm xáo trộn đại diện khi chuyển đổi thành float, nhưng đó không còn là vấn đề nữa.

Để lấy lại số thập phân trong Python:

Decimal(str(value))

Giải pháp này được gợi ý trong tài liệu Python 3.0 về số thập phân :

Để tạo một số thập phân từ một số float, trước tiên hãy chuyển đổi nó thành một chuỗi.


2
Điều này không "cố định" trong Python 3. Chuyển đổi thành float nhất thiết khiến bạn mất biểu diễn thập phân và sẽ dẫn đến sự khác biệt. Nếu Decimalquan trọng là sử dụng, tôi nghĩ tốt hơn là sử dụng chuỗi.
juanpa.arrivillaga

Tôi tin rằng nó an toàn để làm điều này kể từ python 3.1. Mất độ chính xác có thể có hại trong các phép toán số học, nhưng trong trường hợp mã hóa JSON, bạn chỉ đang tạo ra một chuỗi hiển thị giá trị, do đó độ chính xác là quá đủ cho hầu hết các trường hợp sử dụng. Mọi thứ trong JSON đều là một chuỗi, vì vậy việc đặt dấu ngoặc kép quanh giá trị chỉ bất chấp thông số JSON.
Hugo Mota

Như đã nói, tôi hiểu những lo ngại xung quanh việc chuyển đổi sang float. Có lẽ có một chiến lược khác để sử dụng với bộ mã hóa để tạo ra chuỗi hiển thị mong muốn. Tuy nhiên, tôi không nghĩ rằng nó đáng để tạo ra một giá trị được trích dẫn.
Hugo Mota

@HugoMota "Mọi thứ trong JSON đều là một chuỗi, vì vậy việc đặt dấu ngoặc kép xung quanh giá trị chỉ bất chấp thông số JSON." Không: rfc-editor.org/rfc/rfc8259.txt - JSON là định dạng mã hóa dựa trên văn bản, nhưng điều đó không có nghĩa là mọi thứ trong đó sẽ được hiểu là một chuỗi. Thông số kỹ thuật xác định cách mã hóa số, tách biệt với chuỗi.
Gunnar Þór Magnússon

@ GunnarÞórMagnússon "JSON là một định dạng mã hóa dựa trên văn bản" - đó là điều tôi muốn nói với "mọi thứ là một chuỗi". Chuyển đổi các số thành chuỗi trước sẽ không bảo toàn độ chính xác một cách kỳ diệu vì dù sao nó cũng sẽ là một chuỗi khi nó trở thành JSON. Và theo thông số kỹ thuật, các con số không có trích dẫn xung quanh nó. Trách nhiệm của người đọc là giữ gìn sự chính xác trong khi đọc (không phải là trích dẫn, chỉ là do tôi đảm nhận).
Hugo Mota

6

Đây là những gì tôi có, trích từ lớp chúng tôi

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)

Mà vượt qua không đáng tin cậy:

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)bình luận nên làjson.loads(myString, cls=CommonJSONDecoder)
Can Kavaklıoğlu

object_hook cần một giá trị trả về mặc định nếu obj không phải là số thập phân.
Can Kavaklıoğlu

3

Bạn có thể tạo một bộ mã hóa JSON tùy chỉnh theo yêu cầu của bạn.

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)

Bộ giải mã có thể được gọi như thế này,

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

và đầu ra sẽ là:

>>'{"x": 3.9}'

tuyệt vời ... Cảm ơn vì giải pháp một cửa (y)
húng quế

Điều này thực sự hoạt động! Cảm ơn bạn đã chia sẻ giải pháp của bạn
tthreetorch

3

Đối với những người không muốn sử dụng thư viện của bên thứ ba ... Một vấn đề với câu trả lời của Elias Zamaria là nó chuyển thành nổi, có thể gặp vấn đề. Ví dụ:

>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 1e-07}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01733}'

Các JSONEncoder.encode()phương pháp cho phép bạn trả về nội dung json đen, không giống như JSONEncoder.default(), đã bạn quay trở lại một loại tương thích json (như phao) mà sau đó được mã hóa theo cách thông thường. Vấn đề encode()là nó (bình thường) chỉ hoạt động ở cấp cao nhất. Nhưng nó vẫn có thể sử dụng được, với một chút công việc phụ (python 3.x):

import json
from collections.abc import Mapping, Iterable
from decimal import Decimal

class DecimalEncoder(json.JSONEncoder):
    def encode(self, obj):
        if isinstance(obj, Mapping):
            return '{' + ', '.join(f'{self.encode(k)}: {self.encode(v)}' for (k, v) in obj.items()) + '}'
        if isinstance(obj, Iterable) and (not isinstance(obj, str)):
            return '[' + ', '.join(map(self.encode, obj)) + ']'
        if isinstance(obj, Decimal):
            return f'{obj.normalize():f}'  # using normalize() gets rid of trailing 0s, using ':f' prevents scientific notation
        return super().encode(obj)

Cung cấp cho bạn:

>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 0.0000001}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01734}'

2

Dựa trên câu trả lời stdOrgnlDave tôi đã xác định trình bao bọc này có thể được gọi với các loại tùy chọn để bộ mã hóa sẽ chỉ hoạt động cho một số loại nhất định trong các dự án của bạn. Tôi tin rằng công việc nên được thực hiện bên trong mã của bạn và không sử dụng bộ mã hóa "mặc định" này vì "nó rõ ràng hơn ẩn", nhưng tôi hiểu việc sử dụng điều này sẽ tiết kiệm thời gian của bạn. :-)

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()

0

Nếu bạn muốn chuyển một từ điển chứa số thập phân vào requeststhư viện (sử dụng jsonđối số từ khóa), bạn chỉ cần cài đặt 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')})

Lý do của vấn đề là chỉ requestssử dụng simplejsonnếu nó có mặt và rơi trở lại tích hợp jsonnếu nó không được cài đặt.


-6

điều này có thể được thực hiện bằng cách thêm

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

trong \Lib\json\encoder.py:JSONEncoder._iterencode, nhưng tôi đã hy vọng cho một giải pháp tốt hơn


5
Bạn có thể phân lớp JSONEncoder như đã kiểm tra ở trên, chỉnh sửa các tệp Python đã cài đặt của một thư viện đã thiết lập hoặc chính trình thông dịch sẽ là giải pháp cuối cùng.
justanr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.