पायथन जॉन्सन एक दशमलव वस्तु को क्रमबद्ध करता है


242

मेरे पास एक Decimal('3.9')ऑब्जेक्ट का एक हिस्सा है, और इसे JSON स्ट्रिंग में एन्कोड करने की इच्छा है जो कि दिखना चाहिए {'x': 3.9}। मैं ग्राहक पक्ष पर सटीकता के बारे में परवाह नहीं करता हूं, इसलिए एक फ्लोट ठीक है।

क्या यह धारावाहिक करने का एक अच्छा तरीका है? JSONDecoder दशमलव ऑब्जेक्ट्स को स्वीकार नहीं करता है, और पहले से एक फ्लोट में परिवर्तित होता है {'x': 3.8999999999999999}जो कि गलत है, और बैंडविड्थ की एक बड़ी बर्बादी होगी।



3.8999999999999999 3.4 है से अधिक गलत नहीं है। 0.2 का कोई सटीक फ्लोट प्रतिनिधित्व नहीं है।
जैसन

@Jasen 3.89999999999 3.4 के मुकाबले 12.8% अधिक गलत है। JSON मानक केवल क्रमांकन और अंकन के बारे में है, कार्यान्वयन नहीं। IEEE754 का उपयोग करना कच्चे JSON युक्ति का हिस्सा नहीं है, यह इसे लागू करने का सबसे आम तरीका है। एक कार्यान्वयन जो केवल सटीक दशमलव अंकगणित का उपयोग करता है वह पूरी तरह से (वास्तव में, और भी सख्ती से) अनुरूप है।
hraban

😂 कम गलत। विडंबना।
hraban

जवाबों:


147

उपवर्ग के बारे में कैसे 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)

फिर इसे इस तरह उपयोग करें:

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

आउच, मैंने अभी देखा कि यह वास्तव में इस तरह काम नहीं करेगा। तदनुसार संपादित करेंगे। (विचार एक ही है, हालांकि रहता है।)
मीकल Marczyk

समस्या यह थी कि DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()सही लौटा '3.9', लेकिन DecimalEncoder()._iterencode(3.9).next()एक जनरेटर ऑब्जेक्ट लौटाया जो केवल तभी वापस आएगा '3.899...'जब आप दूसरे पर ढेर कर देंगे .next()। जेनरेटर मज़ेदार व्यवसाय। ओह ठीक है ... अब काम करना चाहिए।
माइकल मार्ज़ेक

8
क्या आप इसके return (str(o),)बजाय नहीं कर सकते ? [o]केवल 1 तत्व के साथ एक सूची है, इस पर लूपिंग क्यों परेशान करती है?
एमपीएन

2
@ मर्क: return (str(o),)लंबाई 1 की वापसी होगी, जबकि उत्तर में कोड लंबाई 1 का जनरेटर होता है। इटर्केनोड () डॉक्स
अबगन

30
यह कार्यान्वयन अब काम नहीं करता है। इलायस ज़मरिया एक ही शैली पर काम करने वाला है।
पीरो

223

Simplejson 2.1 और उच्चतर में दशमलव प्रकार के लिए मूल समर्थन है:

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

ध्यान दें कि use_decimalहै Trueडिफ़ॉल्ट रूप से:

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

इसलिए:

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

उम्मीद है, इस सुविधा को मानक पुस्तकालय में शामिल किया जाएगा।


7
हम्म, मेरे लिए यह दशमलव वस्तुओं को तैरने के लिए परिवर्तित करता है, जो स्वीकार्य नहीं है। उदाहरण के लिए, मुद्रा के साथ काम करते समय परिशुद्धता का नुकसान।
मैथ्यू Schinckel

12
@MatthewSchinckel मुझे लगता है कि यह नहीं है। यह वास्तव में इससे एक तार बनाता है। और यदि आप परिणामी स्ट्रिंग को वापस फीड करते हैं तो json.loads(s, use_decimal=True)यह आपको दशमलव वापस देता है। पूरी प्रक्रिया में कोई फ्लोट नहीं। उत्तर के ऊपर संपादित किया गया। आशा है कि मूल पोस्टर इसके साथ ठीक है।
शेखर

1
अहा, मुझे लगता है कि मैं use_decimal=Trueलोड पर भी उपयोग नहीं कर रहा था ।
मैथ्यू Schinckel

1
मेरे लिए json.dumps({'a' : Decimal('3.9')}, use_decimal=True)देता है '{"a": 3.9}'। क्या लक्ष्य नहीं था '{"a": "3.9"}'?
MrJ

5
simplejson.dumps(decimal.Decimal('2.2'))यह भी काम करता है: कोई स्पष्ट use_decimal(simplejson / 3.6.0 पर परीक्षण)। इसे वापस लोड करने का एक और तरीका है: json.loads(s, parse_float=Decimal)यानी, आप इसे stdlib का उपयोग करके पढ़ सकते हैं json(और पुराने simplejsonसंस्करण भी समर्थित हैं)।
JFS

181

मैं सभी को यह बताना चाहूंगा कि मैंने अपने वेब सर्वर पर माइकेल मार्कज़ी के उत्तर की कोशिश की जो पायथन 2.6.5 चला रहा था और इसने ठीक काम किया। हालांकि, मैंने पायथन 2.7 में अपग्रेड किया और इसने काम करना बंद कर दिया। मैंने दशमलव वस्तुओं को एनकोड करने के कुछ प्रकार के बारे में सोचने की कोशिश की और यही मैं साथ आया:

import decimal

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

यह उम्मीद करता है कि किसी को भी मदद करनी चाहिए, जिसे पाइथन 2.7 से समस्या है। मैंने इसका परीक्षण किया और यह ठीक काम करने लगता है। अगर किसी ने मेरे समाधान में किसी भी कीड़े को नोटिस किया है या बेहतर तरीके से आता है, तो कृपया मुझे बताएं।


4
पायथन 2.7 ने फ़्लोटिंग फ़्लोटिंग के नियमों को बदल दिया ताकि यह काम करे। Stackoverflow.com/questions/1447287/…
Nelson

2
हममें से जो लोग सिंपलसन (यानी Google App Engine पर) का उपयोग नहीं कर सकते, उनके लिए यह उत्तर एक Godsend है।
जोएल क्रॉस

17
सटीक सुनिश्चित करने के लिए उपयोग करें unicodeया strइसके बजाय float
सेपो एरविला

2
54.3999 के साथ समस्या ... पायथन 2.6.x में महत्वपूर्ण थी और पुराने जहां रूपांतरण को स्ट्रिंग में नियमित रूप से काम नहीं किया गया था, लेकिन रूपांतरण दशमलव को बहुत अधिक गलत है क्योंकि यह दोहरे उद्धरण चिह्नों के साथ स्ट्रिंग के रूप में क्रमबद्ध होगा "54.4", जैसा कि नहीं एक संख्या।
हाइनेसर

1
Python3 में काम करता है
SeanFromIT

43

मेरे फ्लास्क ऐप में, जो अजगर 2.7.11 का उपयोग करता है, फ्लास्क कीमिया ('db.decimal' प्रकार के साथ), और फ्लास्क मार्शमैलो ('इंस्टेंट' सीरियलाइज़र और डिसेरिएलाइज़र के लिए), मेरे पास यह त्रुटि थी, हर बार जब मैंने GET या POST किया। । क्रमिक और डेज़राइज़र, दशमलव प्रकारों को किसी भी JSON पहचान योग्य प्रारूप में परिवर्तित करने में विफल रहा।

मैंने "पाइप इंस्टॉल सिंपलजोन" किया, फिर बस जोड़कर

import simplejson as json

धारावाहिक और निर्जनता फिर से शुरू हो जाता है। मैंने और कुछ नहीं किया ... DEciamls को '234.00' फ्लोट प्रारूप के रूप में प्रदर्शित किया गया है।


1
सबसे आसान तय
SMDC

1
ताज्जुब है, तुम भी आयात करने की जरूरत नहीं है simplejson- बस इसे स्थापित करने की कोशिश करता है। प्रारंभ में इस उत्तर द्वारा उल्लेख किया गया है ।
bsplosion

यह मुझ पर काम नहीं करता है, और फिर भी Decimal('0.00') is not JSON serializable इसे पाइप के माध्यम से स्थापित करने के बाद मिला है । यह स्थिति तब है जब आप मार्शमैलो और ग्राफीन दोनों का उपयोग कर रहे हैं। जब एक क्वेरी को बाकी एपीई पर बुलाया जाता है, तो मार्शमैलो दशमलव क्षेत्रों के लिए अपेक्षित रूप से काम करता है। हालाँकि जब इसे ग्रेफ्ल के साथ बुलाया जाता है तो इसमें एक is not JSON serializableत्रुटि होती है।
Roel

शानदार, शानदार,
स्पाइडरमैन

उत्तम! यह उन स्थितियों में काम करता है जहां आप किसी अन्य व्यक्ति द्वारा लिखे गए मॉड्यूल का उपयोग कर रहे हैं जिसे आप आसानी से संशोधित नहीं कर सकते हैं (Google शीट्स का उपयोग करने के लिए मेरे मामले में)
हैप्पीसेप्टिक

32

मैंने जीएई 2.7 के लिए सिम्पलसन से बिल्टिन जोंस में स्विच करने की कोशिश की, और दशमलव के साथ समस्या थी। यदि डिफ़ॉल्ट लौट आए str (o) उद्धरण थे (क्योंकि _iterencode डिफ़ॉल्ट के परिणामों पर _iterencode कॉल करता है), और float (o) ट्रेलिंग 0 को हटा देगा।

यदि डिफ़ॉल्ट एक वर्ग की एक वस्तु देता है जो फ्लोट से विरासत में मिला है (या कुछ भी जो अतिरिक्त स्वरूपण के बिना repr कहता है) और एक कस्टम __repr__ विधि है, तो यह काम करना चाहता है जैसे मैं चाहता हूं।

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

अच्छा! यह सुनिश्चित करता है कि दशमलव मान JSON में जावास्क्रिप्ट फ्लोट के रूप में समाप्त होता है, पायथन पहले राउंड फ्लोट मान के बिना होने के बिना।
कोनराड

3
दुर्भाग्य से यह हाल ही में पायथन 3 में काम नहीं करता है। अब कुछ फास्ट-पाथ कोड है जो सभी फ्लोट-उपवर्गों को फ्लोट के रूप में मानता है, और उन पर पूरी तरह से कॉल नहीं करता है।
अंती हापला

@AnttiHaapala, उदाहरण पायथन 3.6 पर ठीक काम करता है।
क्रिस्टियन सियुपिटु

@CristianCiupitu वास्तव में, मुझे अब बुरे व्यवहार को पुन: पेश करने में सक्षम नहीं होना है
एंटी हापला

2
समाधान v3.5.2rc1 के बाद से काम करना बंद कर देता है, github.com/python/cpython/commit/… देखें । वहाँ है float.__repr__hardcoded (कि परिशुद्धता खो देता है), और fakefloat.__repr__सभी को नहीं बुलाया गया है। ऊपर दिए गए समाधान python3 के लिए 3.5.1 तक ठीक से काम करता है, यदि नकली विधि में अतिरिक्त विधि है def __float__(self): return self
मिरोस्लाव

30

मूल विकल्प गायब है, इसलिए मैं इसे अगले आदमी / पित्त के लिए जोड़ूंगा जो इसके लिए दिखता है।

Django 1.7.x पर शुरू एक अंतर्निहित है DjangoJSONEncoderजिसमें आप इसे प्राप्त कर सकते हैं 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!


हालांकि यह जानना बहुत अच्छा है, ओपी ने जिंजो के बारे में नहीं पूछा?
std''OrgnlDave

4
@ std''OrgnlDave आप 100% सही हैं। मैं भूल गया कि मैं यहाँ कैसे आया, लेकिन मैंने इस शब्द को खोज शब्द से जुड़े "django" के साथ जोड़ा और यह बात सामने आई, थोड़ी और गुगली करने के बाद, मैंने इसका उत्तर ढूंढ लिया और इसे अपने जैसे अगले व्यक्ति के लिए यहाँ जोड़ दिया, जो उस पार पहुँच गया। यह
जेवियर बुज़ी

6
आप मेरा दिन
बचाते

14

मेरी $ .02!

जब से मैं अपने वेब सर्वर के लिए टन डेटा अनुक्रमित कर रहा हूं, मैं JSON एनकोडर का एक गुच्छा बढ़ाता हूं। यहाँ कुछ अच्छा कोड है। ध्यान दें कि यह बहुत ही आसानी से किसी भी डेटा प्रारूप का विस्तार करने योग्य है जिसे आप महसूस करते हैं और 3.9 के रूप में पुन: पेश करेंगे"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

मेरे जीवन को इतना आसान बना देता है ...


3
यह गलत है: यह 3.9 को पुन: पेश करेगा "thing": "3.9"
ग्लीफ

सभी का सबसे अच्छा समाधान, बहुत सरल, धन्यवाद आपने मेरा दिन बचाया, मेरे लिए संख्या को बचाने के लिए पर्याप्त है, स्ट्रिंग में दशमलव के लिए ठीक है
स्टेन्डडेव

JSON मानकों के माध्यम से @Glyph (जिनमें से कुछ हैं ...), एक अनक्वालेटेड संख्या एक डबल-सटीक फ़्लोटिंग-पॉइंट है, न कि एक दशमलव संख्या। संगतता की गारंटी देने के लिए इसे उद्धृत करना एकमात्र तरीका है।
std''OrgnlDave

2
क्या आपके पास इसके लिए एक प्रशस्ति पत्र है? मैंने पढ़ा हर कल्पना का तात्पर्य है कि यह कार्यान्वयन-निर्भर है।
ग्लिफ़

12

3.9आईईईई फ्लोट्स में बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है, यह हमेशा की तरह आएगा 3.8999999999999999, उदाहरण के लिए print repr(3.9), आप यहां इसके बारे में अधिक पढ़ सकते हैं:

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

इसलिए यदि आप फ्लोट नहीं चाहते हैं, तो केवल विकल्प आपको इसे स्ट्रिंग के रूप में भेजना है, और जेन्सन में दशमलव वस्तुओं के स्वचालित रूपांतरण की अनुमति देने के लिए कुछ इस तरह से करें:

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 नहीं होगा, लेकिन 3.9 एक वैध JSON फ्लोट है। यानी, json.loads("3.9")काम करेगा, और मैं चाहूंगा कि यह हो
Knio

@ अनुराग का मतलब है आप अपने उदाहरण में repr (o) के बजाय repr (obj) थे।
ओरोकुसाकी

यदि आप कोशिश करते हैं और दशमलव नहीं है, तो यह सिर्फ मरना नहीं होगा?
मिकेमाकाना

1
@ नाविक, नहीं, यह आप कोशिश कर सकते हैं, कारण डिफ़ॉल्ट रूप से संकेत को अपवाद बढ़ाता है कि अगले हैंडलर का उपयोग किया जाना चाहिए
अनुराग उनियाल

1
Mikez302 का जवाब देखें - पायथन 2.7 या इसके बाद के संस्करण में, यह अब लागू नहीं होता है।
जोएल क्रॉस

9

Django उपयोगकर्ताओं के लिए :

हाल ही में TypeError: Decimal('2337.00') is not JSON serializable JSON एन्कोडिंग यानी भर में आया थाjson.dumps(data)

समाधान :

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

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

लेकिन, अब दशमलव मान एक स्ट्रिंग होगा, अब हम स्पष्ट रूप से डेटा को डिकोड करते समय दशमलव / फ्लोट मान पार्सर को parse_floatविकल्प में उपयोग करके सेट कर सकते हैं json.loads:

import decimal 

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

8

JSON.org से लिंक किए गए JSON मानक दस्तावेज़ से :

JSON संख्याओं के शब्दार्थ के बारे में अज्ञेयवादी है। किसी भी प्रोग्रामिंग भाषा में, कई प्रकार की विभिन्न प्रकार की क्षमताएं और पूरक, फिक्स्ड या फ्लोटिंग, बाइनरी या दशमलव हो सकते हैं। जो विभिन्न प्रोग्रामिंग भाषाओं के बीच इंटरचेंज को कठिन बना सकता है। JSON इसके बजाय केवल उन संख्याओं का प्रतिनिधित्व प्रदान करता है जिनका मानव उपयोग करता है: अंकों का एक क्रम। सभी प्रोग्रामिंग भाषाएं जानती हैं कि अंकों के अनुक्रम को कैसे बनाना है, भले ही वे आंतरिक अभ्यावेदन पर असहमत हों। यह इंटरचेंज की अनुमति देने के लिए पर्याप्त है।

तो यह वास्तव में JSON में संख्याओं (स्ट्रिंग के बजाय) के रूप में दशमलव का प्रतिनिधित्व करने के लिए सटीक है। Bellow समस्या का एक संभावित समाधान निहित है।

एक कस्टम JSON एनकोडर को परिभाषित करें:

import json


class CustomJsonEncoder(json.JSONEncoder):

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

फिर अपने डेटा को क्रमबद्ध करते समय इसका उपयोग करें:

json.dumps(data, cls=CustomJsonEncoder)

जैसा कि अन्य उत्तरों पर टिप्पणियों से उल्लेख किया गया है, अजगर के पुराने संस्करण फ्लोट में परिवर्तित होने पर प्रतिनिधित्व को गड़बड़ कर सकते हैं, लेकिन अब ऐसा नहीं है।

दशमलव को पायथन में वापस लाने के लिए:

Decimal(str(value))

यह समाधान पायथन में दशमलव 3.0 प्रलेखन में संकेत दिया गया है :

एक फ्लोट से दशमलव बनाने के लिए, पहले इसे एक स्ट्रिंग में बदलें।


2
यह पायथन में "निश्चित" नहीं है। 3. float आवश्यक रूप से परिवर्तित करने से आप दशमलव प्रतिनिधित्व खो देते हैं, और विसंगतियों को जन्म देगा । यदि Decimalउपयोग करना महत्वपूर्ण है, तो मुझे लगता है कि स्ट्रिंग्स का उपयोग करना बेहतर है।
जुआनपा। श्रीविल्लागा

मेरा मानना ​​है कि अजगर 3.1 के बाद से ऐसा करना सुरक्षित है। परिशुद्धता में नुकसान अंकगणितीय कार्यों में हानिकारक हो सकता है, लेकिन JSON एन्कोडिंग के मामले में, आप केवल मान का एक स्ट्रिंग डिस्प्ले तैयार कर रहे हैं, इसलिए अधिकांश उपयोग मामलों के लिए सटीक पर्याप्त से अधिक है। JSON में सब कुछ पहले से ही एक स्ट्रिंग है, इसलिए मूल्य के चारों ओर उद्धरण डालना सिर्फ JSON कल्पना को परिभाषित करता है।
ह्यूगो मोटा

इसके साथ ही मैंने कहा कि मैं तैरने में परिवर्तित होने वाली चिंताओं को समझता हूं। वांछित प्रदर्शन स्ट्रिंग का उत्पादन करने के लिए एनकोडर के साथ उपयोग करने के लिए संभवतः एक अलग रणनीति है। फिर भी, मुझे नहीं लगता कि यह एक उद्धृत मूल्य का उत्पादन करने लायक है।
ह्यूगो मोटा

@HugoMota "JSON में सब कुछ पहले से ही एक स्ट्रिंग है, इसलिए मूल्य के चारों ओर उद्धरण डालना सिर्फ JSON कल्पना को परिभाषित करता है।" नहीं: rfc-editor.org/rfc/rfc8259.txt - JSON एक टेक्स्ट-आधारित एन्कोडिंग प्रारूप है, लेकिन इसका मतलब यह नहीं है कि इसमें सब कुछ एक स्ट्रिंग के रूप में व्याख्या किया जाना है। युक्ति यह परिभाषित करती है कि स्ट्रिंग्स से अलग, संख्याओं को कैसे एनकोड किया जाए।
गुन्नार Gunór मैगनसन

@ Gunnar JórMagnússon "JSON एक टेक्स्ट-आधारित एन्कोडिंग प्रारूप है" - यही मेरा मतलब है "सब कुछ एक स्ट्रिंग है"। संख्याओं को पहले से स्ट्रिंग में बदलना जादुई रूप से सटीकता को संरक्षित नहीं करेगा क्योंकि यह JSON बनने पर वैसे भी एक स्ट्रिंग होगी। और युक्ति के अनुसार, संख्याओं के आसपास इसके उद्धरण नहीं हैं। यह पढ़ने के दौरान सटीकता को संरक्षित करने के लिए पाठक की ज़िम्मेदारी है (उद्धरण नहीं, बस इस पर मेरा ध्यान रखें)।
ह्यूगो मोटा

6

यह वही है जो मेरे पास है, हमारी कक्षा से निकाला गया है

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)

जो सबसे पास है:

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)टिप्पणी होनी चाहिएjson.loads(myString, cls=CommonJSONDecoder)
14

अगर ऑब्जेक्ट दशमलव नहीं है, तो object_hook को डिफ़ॉल्ट रिटर्न मान की आवश्यकता होती है।
कैनवलीओलू

3

आप अपनी आवश्यकता के अनुसार एक कस्टम JSON एनकोडर बना सकते हैं।

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)

डिकोडर को इस तरह कहा जा सकता है,

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

और उत्पादन होगा:

>>'{"x": 3.9}'

कमाल ... एक स्टॉप सॉल्यूशन (y) के लिए धन्यवाद
मुहम्मद तुलसी

यह वास्तव में काम करता है! अपना समाधान साझा करने के लिए धन्यवाद
tthreetorch

3

उन लोगों के लिए जो तीसरे पक्ष के पुस्तकालय का उपयोग नहीं करना चाहते हैं ... एलियास ज़मरिया के जवाब के साथ एक मुद्दा यह है कि यह फ्लोट में परिवर्तित हो जाता है, जो समस्याओं में चल सकता है। उदाहरण के लिए:

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

JSONEncoder.encode()विधि आप शाब्दिक json सामग्री वापस देती है, जबकि JSONEncoder.default(), जो आप (नाव) की तरह एक json संगत प्रकार वापसी की है कि उसके बाद सामान्य तरीके से इनकोडिंग हो जाता है। इसके साथ समस्या encode()यह है कि यह (सामान्य रूप से) केवल शीर्ष स्तर पर काम करता है। लेकिन यह अभी भी प्रयोग करने योग्य है, थोड़ा अतिरिक्त काम (अजगर 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)

जो आपको देता है:

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

2

StdOrgnlDave उत्तर के आधार पर मैंने इस आवरण को परिभाषित किया है कि इसे वैकल्पिक प्रकारों के साथ कहा जा सकता है, इसलिए एनकोडर केवल कुछ प्रकार के लिए आपके प्रोजेक्ट्स के अंदर काम करेगा। मेरा मानना ​​है कि आपके कोड के अंदर काम किया जाना चाहिए और इस "डिफ़ॉल्ट" एनकोडर का उपयोग नहीं करना चाहिए क्योंकि "यह अंतर्निहित से बेहतर है", लेकिन मैं समझता हूं कि इसका उपयोग करने से आपका कुछ समय बच जाएगा। :-)

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

यदि आप एक डिक्शनरी युक्त डिस्क्रिप्शन को requestsलाइब्रेरी में पास करना चाहते हैं ( jsonकीवर्ड तर्क का उपयोग करके ), तो आपको बस इंस्टॉल करना होगा 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')})

समस्या का कारण यह है कि इसका requestsउपयोग simplejsonकेवल तभी होता है यदि यह मौजूद है, और अंतर्निहित में वापस गिर जाता है jsonयदि यह स्थापित नहीं है।


-6

इसे जोड़कर किया जा सकता है

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

में \Lib\json\encoder.py:JSONEncoder._iterencode, लेकिन मैं एक बेहतर समाधान की उम्मीद कर रहा था


5
आप जेएसओएनएनकोडर को उपर्युक्त के रूप में जांच सकते हैं, किसी स्थापित पुस्तकालय की स्थापित पायथन फ़ाइलों को संपादित करना या दुभाषिया ही बहुत अंतिम उपाय होना चाहिए।
justanr
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.