JSON के वर्ग उदाहरण को सीरियल करना


185

मैं एक वर्ग उदाहरण के JSON स्ट्रिंग प्रतिनिधित्व बनाने और कठिनाई होने की कोशिश कर रहा हूं। मान लीजिए कि वर्ग इस तरह बनाया गया है:

class testclass:
    value1 = "a"
    value2 = "b"

Json.dumps पर कॉल इस तरह किया जाता है:

t = testclass()
json.dumps(t)

यह विफल हो रहा है और मुझे बता रहा है कि टेस्टक्लास JSON अनुक्रमिक नहीं है।

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable

मैंने अचार मॉड्यूल का उपयोग करने की भी कोशिश की है:

t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))

और यह वर्ग उदाहरण की जानकारी देता है, लेकिन वर्ग उदाहरण की क्रमबद्ध सामग्री नहीं।

b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'

मैं क्या गलत कर रहा हूं?



30
एक पंक्ति, का प्रयोग करें s = json.dumps(obj, default=lambda x: x.__dict__), serialize वस्तु के उदाहरण चर के ( self.value1, self.value2, ...)। इसका सबसे सरल और सबसे सीधा तरीका है। यह नेस्टेड ऑब्जेक्ट संरचनाओं को क्रमबद्ध करेगा। defaultसमारोह कहा जाता है जब किसी भी वस्तु सीधे serializable नहीं है। आप नीचे मेरे उत्तर को भी देख सकते हैं। मुझे लोकप्रिय उत्तर अनावश्यक रूप से जटिल लगे, जो शायद काफी समय पहले सच थे।
कोडमेनि48

1
आपके testclassपास कोई __init__()विधि नहीं है, इसलिए सभी उदाहरण वर्ग विवरण में परिभाषित समान दो वर्ग विशेषताओं ( value1और value2) को साझा करेंगे । क्या आप एक वर्ग और एक के बीच के अंतर को समझते हैं?
मार्टिउ

1
इस github.com/jsonpickle/jsonpickle के लिए एक अजगर पुस्तकालय है (टिप्पणी चूंकि उत्तर धागा में बहुत नीचे है और अभ्यस्त नहीं है।)
शुभकामनाएं

जवाबों:


237

मूल समस्या यह है कि JSON एनकोडर json.dumps()केवल एक प्रकार का ऑब्जेक्ट सेट डिफ़ॉल्ट रूप से, सभी अंतर्निहित प्रकारों को क्रमबद्ध करना जानता है। यहाँ सूची: https://docs.python.org/3.3/library/json.html#encoders-and-decoders

एक अच्छा समाधान यह होगा कि आप अपनी कक्षा को इनहेरिट करें JSONEncoderऔर फिर JSONEncoder.default()फ़ंक्शन को लागू करें , और उस फ़ंक्शन को अपनी कक्षा के लिए सही JSON का उत्सर्जन करें।

एक सरल समाधान उस उदाहरण json.dumps()के .__dict__सदस्य को कॉल करना होगा । यह एक मानक पायथन है dictऔर यदि आपकी कक्षा सरल है तो यह JSON क्रमिक होगा।

class Foo(object):
    def __init__(self):
        self.x = 1
        self.y = 2

foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"

s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}

इस ब्लॉग पोस्टिंग में उपरोक्त दृष्टिकोण पर चर्चा की गई है:

    __Dict__ का उपयोग करके JSON के लिए मनमानी अजगर वस्तुओं को सीरियल करना


3
मैंने यह कोशिश की। Json.dumps (t .__ dict__) पर कॉल करने का अंतिम परिणाम सिर्फ {} है।
फेरन

6
ऐसा इसलिए है क्योंकि आपकी कक्षा में एक .__init__()विधि कार्य नहीं है , इसलिए आपके कक्षा के उदाहरण में एक खाली शब्दकोष है। दूसरे शब्दों में, {}आपके उदाहरण कोड का सही परिणाम है।
स्टीवेहा

3
धन्यवाद। यह ट्रिक करता है। मैं एक साधारण जोड़ा init कोई पैरामीटर के साथ और अब json.dumps बुला (टी .__ dict__) के प्रारूप में उचित डेटा देता है: { "मान 2": "345", "मान 1": "123"} मैं जैसे पदों देखा था इससे पहले, यह निश्चित नहीं था कि मुझे सदस्यों के लिए एक कस्टम धारावाहिक की आवश्यकता है, init की आवश्यकता स्पष्ट रूप से उल्लेख नहीं की गई थी या मैं इसे याद नहीं करता था। धन्यवाद।
फेरन

3
यह एक एकल वर्ग के लिए काम करता है, लेकिन संबंधित कक्षाओं के साथ नहीं
obawel A Iroume

2
@NwawelAIroume: सच है। यदि आपके पास एक वस्तु है जो उदाहरण के लिए एक सूची में कई वस्तुओं से युक्त है, तो त्रुटि अभी भी हैis not JSON serializable
gies0r

57

एक तरीका है जो मेरे लिए बहुत अच्छा काम करता है जिसे आप आज़मा सकते हैं:

json.dumps()एक वैकल्पिक पैरामीटर डिफ़ॉल्ट ले जा सकता है जहाँ आप अज्ञात प्रकारों के लिए एक कस्टम क्रमिक फ़ंक्शन निर्दिष्ट कर सकते हैं, जो मेरे मामले में दिखता है

def serialize(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, date):
        serial = obj.isoformat()
        return serial

    if isinstance(obj, time):
        serial = obj.isoformat()
        return serial

    return obj.__dict__

पहले दो इफ्स डेट और टाइम सीरियलाइजेशन के लिए होते हैं और फिर obj.__dict__किसी अन्य ऑब्जेक्ट के लिए लौटा दिए जाते हैं।

अंतिम कॉल ऐसा लगता है:

json.dumps(myObj, default=serialize)

जब आप किसी संग्रह को क्रमबद्ध कर रहे हों तो यह विशेष रूप से अच्छा है और आप __dict__प्रत्येक वस्तु के लिए स्पष्ट रूप से कॉल नहीं करना चाहते हैं । यहाँ यह आपके लिए स्वचालित रूप से किया गया है।

अब तक मेरे लिए बहुत अच्छा काम किया, आपके विचारों के लिए तत्पर हूं।


मुझे मिलता है NameError: name 'serialize' is not defined। कोई सुझाव?
काइल डेलानी

बहुत अच्छा। बस उन वर्गों के लिए जिनके पास स्लॉट हैं:try: dict = obj.__dict__ except AttributeError: dict = {s: getattr(obj, s) for s in obj.__slots__ if hasattr(obj, s)} return dict
फंतासी

यह आश्चर्यजनक है कि इस तरह की एक लोकप्रिय भाषा में एक वस्तु के लिए कोई लाइनर नहीं है। ऐसा होना चाहिए क्योंकि यह सांख्यिकीय रूप से टाइप नहीं किया गया है।
रेनेन

48

आप फ़ंक्शन defaultमें नामित पैरामीटर निर्दिष्ट कर सकते हैं json.dumps():

json.dumps(obj, default=lambda x: x.__dict__)

स्पष्टीकरण:

डॉक्स फॉर्म ( 2.7 , 3.6 ):

``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.

(पाइथन 2.7 और पाइथन 3.x पर काम करता है)

नोट: इस मामले में आपको instanceचर की आवश्यकता है और classचर की नहीं , जैसा कि प्रश्न में उदाहरण करने की कोशिश करता है। (मैं पूछ रहा हूँ कि पूछने class instanceवाला एक वर्ग का एक उद्देश्य है)

मैं @ phihag के जवाब से पहले सीखा यहाँ । यह काम करने का सबसे सरल और साफ तरीका है।


6
यह मेरे लिए काम करता है, लेकिन डेटाइमटाइम के सदस्यों की वजह से मैंने इसे थोड़ा बदल दिया:default=lambda x: getattr(x, '__dict__', str(x))
डकोटा हॉकिंस

@ डकोटा अच्छा काम के आसपास; datetime.dateएक C कार्यान्वयन है इसलिए इसकी कोई __dict__विशेषता नहीं है। एकरूपता के लिए datetime.date
आईएमएचओ

22

मैं बस करता हूँ:

data=json.dumps(myobject.__dict__)

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

एक है कि यह वास्तव में अच्छी तरह से काम करता है "विकल्प" वर्ग है जो आपको OptionParser मॉड्यूल से मिलता है। यहाँ यह JSON अनुरोध के साथ है।

  def executeJson(self, url, options):
        data=json.dumps(options.__dict__)
        if options.verbose:
            print data
        headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
        return requests.post(url, data, headers=headers)

यदि आप इसका उपयोग किसी कक्षा के अंदर नहीं कर रहे हैं, तो आप स्वयं को हटाना चाह सकते हैं।
स्पैल रेल

3
यह तब तक ठीक रहेगा, जब तक कि वस्तु अन्य वस्तुओं से बनी न हो।
हेरोल्डो_कॉ


5

JSON वास्तव में मनमानी अजगर वस्तुओं को क्रमबद्ध करने के लिए नहीं है। यह dictवस्तुओं को क्रमबद्ध करने के लिए बहुत अच्छा है , लेकिन pickleमॉड्यूल वास्तव में वही है जो आपको सामान्य रूप से उपयोग करना चाहिए। से आउटपुट pickleवास्तव में मानव-पठनीय नहीं है, लेकिन इसे ठीक से अनपिक करना चाहिए। यदि आप JSON का उपयोग करने पर जोर देते हैं, तो आप jsonpickleमॉड्यूल की जांच कर सकते हैं , जो एक दिलचस्प हाइब्रिड दृष्टिकोण है।

https://github.com/jsonpickle/jsonpickle


9
अचार के साथ मुझे जो मुख्य समस्या दिखती है, वह है पायथन-विशिष्ट प्रारूप, जबकि JSON एक प्लेटफ़ॉर्म-इंडिपेंडेंट प्रारूप है। JSON विशेष रूप से उपयोगी है यदि आप या तो एक वेब एप्लिकेशन या कुछ मोबाइल एप्लिकेशन के लिए बैकएंड लिख रहे हैं। कहा जा रहा है, jsonpickle को इंगित करने के लिए धन्यवाद।
हेरोल्डो_कॉ

@Haroldo_OK क्या जेन्सपिकल अभी भी जेन्सन को निर्यात नहीं करता है, बस बहुत ही मानव पठनीय नहीं है?
Caelum

4

यहां किसी भी गैर-परिष्कृत कक्षाओं के क्रमांकन के लिए दो सरल कार्य हैं, जैसा कि पहले बताया गया था, कुछ भी फैंसी नहीं है।

मैं इसका उपयोग कॉन्फ़िगरेशन प्रकार के सामान के लिए करता हूं क्योंकि मैं कक्षाओं में नए सदस्यों को बिना किसी कोड समायोजन के साथ जोड़ सकता हूं।

import json

class SimpleClass:
    def __init__(self, a=None, b=None, c=None):
        self.a = a
        self.b = b
        self.c = c

def serialize_json(instance=None, path=None):
    dt = {}
    dt.update(vars(instance))

    with open(path, "w") as file:
        json.dump(dt, file)

def deserialize_json(cls=None, path=None):
    def read_json(_path):
        with open(_path, "r") as file:
            return json.load(file)

    data = read_json(path)

    instance = object.__new__(cls)

    for key, value in data.items():
        setattr(instance, key, value)

    return instance

# Usage: Create class and serialize under Windows file system.
write_settings = SimpleClass(a=1, b=2, c=3)
serialize_json(write_settings, r"c:\temp\test.json")

# Read back and rehydrate.
read_settings = deserialize_json(SimpleClass, r"c:\temp\test.json")

# results are the same.
print(vars(write_settings))
print(vars(read_settings))

# output:
# {'c': 3, 'b': 2, 'a': 1}
# {'c': 3, 'b': 2, 'a': 1}

3

यह करने के लिए कैसे शुरू करने पर कुछ अच्छे जवाब हैं। लेकिन ध्यान रखने योग्य कुछ बातें हैं:

  • क्या होगा यदि उदाहरण एक बड़े डेटा संरचना के अंदर नेस्टेड है?
  • क्या होगा अगर वर्ग का नाम भी चाहते हैं?
  • क्या होगा अगर आप उदाहरण देना चाहते हैं?
  • यदि आप __slots__इसके बजाय उपयोग कर रहे हैं तो क्या होगा __dict__?
  • क्या होगा यदि आप बस इसे स्वयं नहीं करना चाहते हैं?

json-tricks एक पुस्तकालय है (जो मैंने बनाया और दूसरों ने योगदान दिया) जो काफी समय से ऐसा करने में सक्षम है। उदाहरण के लिए:

class MyTestCls:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

cls_instance = MyTestCls(s='ub', dct={'7': 7})

json = dumps(cls_instance, indent=4)
instance = loads(json)

आपको अपना उदाहरण वापस मिल जाएगा। यहाँ इस तरह दिखता है:

{
    "__instance_type__": [
        "json_tricks.test_class",
        "MyTestCls"
    ],
    "attributes": {
        "s": "ub",
        "dct": {
            "7": 7
        }
    }
}

यदि आप अपना खुद का समाधान करना पसंद करते हैं, तो आप json-tricksकुछ विशेष मामलों (जैसे __slots__) को न भूल पाने के स्रोत को देख सकते हैं ।

यह अन्य प्रकार जैसे सुन्न सरणियों, डेटाटाइम्स, जटिल संख्याओं को भी करता है; यह टिप्पणियों के लिए भी अनुमति देता है।


3

Python3.x

मैं अपने ज्ञान के साथ जो सबसे अच्छी तरह से पहुंच सकता था वह यह था।
ध्यान दें कि यह कोड सेट () भी व्यवहार करता है।
यह दृष्टिकोण सामान्य है बस वर्ग के विस्तार की आवश्यकता है (दूसरे उदाहरण में)।
ध्यान दें कि मैं इसे केवल फ़ाइलों के लिए कर रहा हूं, लेकिन आपके स्वाद के लिए व्यवहार को संशोधित करना आसान है।

हालाँकि यह एक CoDec है।

थोड़े और काम से आप अपनी कक्षा का निर्माण अन्य तरीकों से कर सकते हैं। मैं इसे बनाने के लिए एक डिफॉल्ट कंस्ट्रक्टर मान लेता हूं, फिर मैं वर्ग को अपडेट करता हूं।

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

संपादित करें

कुछ और शोधों के साथ, मुझे एक मेटाक्लर का उपयोग करके SUPERCLASS रजिस्टर विधि कॉल की आवश्यकता के बिना सामान्यीकरण करने का एक तरीका मिला।

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

2

मुझे विश्वास है कि विरासत के बजाय स्वीकार किए गए उत्तर में सुझाव दिया गया है, बहुरूपता का उपयोग करना बेहतर है। अन्यथा आपके पास हर वस्तु के एन्कोडिंग को कस्टमाइज़ करने के लिए एक और स्टेटमेंट होना चाहिए। इसका मतलब है कि JSON के लिए एक सामान्य डिफ़ॉल्ट एनकोडर बनाएं:

def jsonDefEncoder(obj):
   if hasattr(obj, 'jsonEnc'):
      return obj.jsonEnc()
   else: #some default behavior
      return obj.__dict__

और फिर jsonEnc()प्रत्येक वर्ग में एक फ़ंक्शन है जिसे आप क्रमबद्ध करना चाहते हैं। जैसे

class A(object):
   def __init__(self,lengthInFeet):
      self.lengthInFeet=lengthInFeet
   def jsonEnc(self):
      return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter

फिर तुम बुलाओ json.dumps(classInstance,default=jsonDefEncoder)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.