জেএসএনে ক্লাসের উদাহরণটি সিরিয়াল করা হচ্ছে


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__), ধারাবাহিকভাবে বস্তুর দৃষ্টান্ত ভেরিয়েবল করার জন্য ( self.value1, self.value2, ...)। এটি সবচেয়ে সহজ এবং সর্বাধিক সোজা সামনের দিকে। এটি নেস্টেড অবজেক্ট স্ট্রাকচারকে সিরিয়ালাইজ করবে। defaultফাংশন বলা হয় যখন কোনো বস্তুর সরাসরি serializable নয়। আপনি নীচে আমার উত্তর তাকান করতে পারেন। আমি জনপ্রিয় উত্তরগুলি অহেতুক জটিল খুঁজে পেয়েছি, যা সম্ভবত অনেক দিন আগে সত্য ছিল।
কোডম্যান 48

1
আপনার testclassকোনও __init__()পদ্ধতি নেই, সুতরাং সমস্ত দৃষ্টান্ত শ্রেণির বিবৃতিতে সংজ্ঞায়িত একই দুটি শ্রেণির বৈশিষ্ট্য ( value1এবং value2) ভাগ করে নেবে । আপনি কি একটি ক্লাস এবং একটি উদাহরণের মধ্যে পার্থক্য বুঝতে পারেন?
মার্টিনিউ

1
এই github.com/jsonpickle/jsonpickle এর জন্য একটি অজগর গ্রন্থাগার রয়েছে (যেহেতু উত্তরটি থ্রেডের নীচে খুব নীচে রয়েছে এবং মন্তব্যটি পৌঁছতে পারে না))
শুভেচ্ছা

উত্তর:


237

মূল সমস্যাটি হ'ল JSON এনকোডার json.dumps()কেবলমাত্র সমস্ত বিল্ট-ইন প্রকারভেদে ডিফল্টরূপে সীমিত আকারের অবজেক্টের ধরণের সিরিয়ালাইজ করতে জানে knows এখানে তালিকাবদ্ধ করুন: https://docs.python.org/3.3/library/json.html#encoders- এবং- ডিকোডার

একটি ভাল সমাধান হ'ল আপনার ক্লাসটি উত্তরাধিকার সূত্রে উত্তীর্ণ করা JSONEncoderএবং তারপরে JSONEncoder.default()ফাংশনটি বাস্তবায়ন করা এবং সেই ফাংশনটি আপনার ক্লাসের জন্য সঠিক জেএসওএন নির্গমন করা।

একটি সহজ সমাধান হ'ল json.dumps()এই .__dict__দৃষ্টান্তের সদস্যকে ফোন করা । এটি একটি স্ট্যান্ডার্ড পাইথন dictএবং আপনার শ্রেণিটি সহজ হলে এটি জেএসএন সিরিয়ালযোগ্য হবে iz

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 .__ ڈک__) এ কল করার শেষ ফলাফলটি মাত্র {}}
ফারহান

6
এটি কারণ আপনার শ্রেণীর কোনও .__init__()পদ্ধতি ফাংশন নেই, তাই আপনার শ্রেণীর উদাহরণটির একটি খালি অভিধান রয়েছে। অন্য কথায়, {}আপনার উদাহরণ কোডের সঠিক ফলাফল।
স্টিভাহ

3
ধন্যবাদ। এই কৌশলটি করে। আমি একটি সহজ যোগ Init কোন প্যারামিটার দিয়ে এবং এখন json.dumps কলিং (টি .__ dict__) এর বিন্যাসে সঠিক তথ্য ফেরৎ: { "VALUE2": "345", "মান 1": "123"} আমি মত পোস্ট দেখেছিলেন এর আগে, নিশ্চিত করুন কিনা আমি সদস্যদের জন্য একটি কাস্টম serializer প্রয়োজন, প্রয়োজন ছিল না Init স্পষ্টভাবে উল্লেখ করা হয় নি বা আমি এটা মিস করেছি। ধন্যবাদ.
ফেরহান

3
এটি একটি একক শ্রেণীর জন্য কাজ তবে সম্পর্কিত ক্লাসের
আপত্তিগুলির

2
@ নওয়ায়েলএইউরোমে: সত্য। যদি আপনার এমন কোনও বস্তু থাকে যা উদাহরণস্বরূপ তালিকায় একাধিক বস্তু ধারণ করে ত্রুটিটি এখনও রয়েছেis not JSON serializable
gies0r

57

আমার পক্ষে দুর্দান্ত কাজ করার একটি উপায় রয়েছে যা আপনি চেষ্টা করে দেখতে পারেন:

json.dumps()একটি optionচ্ছিক প্যারামিটার ডিফল্ট নিতে পারে যেখানে আপনি অজানা ধরণের জন্য একটি কাস্টম সিরিয়ালাইজ ফাংশন নির্দিষ্ট করতে পারেন, যা আমার ক্ষেত্রে দেখতে ভাল লাগে

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
কল্পনা

এটি আশ্চর্যজনক যে এ জাতীয় জনপ্রিয় ভাষার কোনও বস্তু jsoniny করার জন্য কোনও লাইনার নেই। এটি অবশ্যই হবে কারণ এটি স্ট্যাটিকালি টাইপ করা হয়নি।
TheRennen

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জিজ্ঞাসাকারীকে বোঝাচ্ছি যে কোনও শ্রেণীর একটি বস্তু হতে পারে)

আমি এখানে @ ফিহাগের উত্তরটি থেকে প্রথম শিখেছি । কাজটি করার সবচেয়ে সহজ এবং পরিষ্কার উপায় হিসাবে এটি খুঁজে পেয়েছে।


6
এটি আমার পক্ষে কাজ করেছে, তবে ডেটটাইম ডেট সদস্যের কারণে আমি এটিকে কিছুটা পরিবর্তন করেছি:default=lambda x: getattr(x, '__dict__', str(x))
ডাকোটা হকিন্স

@ ডাকোটা সুন্দর কাজ; datetime.dateএটি একটি সি বাস্তবায়ন তাই এর কোনও __dict__বৈশিষ্ট্য নেই। ইউনিফর্মের জন্য আইএমএইচও, এটি datetime.dateথাকা উচিত ...
কোডম্যান 48

22

আমি শুধু করি:

data=json.dumps(myobject.__dict__)

এটি সম্পূর্ণ উত্তর নয়, এবং আপনার যদি কিছু ধরণের জটিল অবজেক্ট শ্রেণি থাকে তবে আপনি অবশ্যই সবকিছু পাবেন না। তবে আমি এটি আমার কিছু সাধারণ বস্তুর জন্য ব্যবহার করি।

এটি যা অপশনপার্সার মডিউলটি থেকে পেয়েছে এটি "বিকল্পগুলি" শ্রেণিটি যা সত্যিই ভালভাবে কাজ করে তা হ'ল। এখানে এটি নিজেই 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

জেএসএন আসলে পাইথন অবজেক্টকে সিরিয়ালাইজ করার জন্য নয়। এটি dictঅবজেক্ট সিরিয়াল করার জন্য দুর্দান্ত তবে pickleমডিউলটি হ'ল সাধারণভাবে আপনার ব্যবহার করা উচিত। আউটপুট pickleসত্যই মানব-পঠনযোগ্য নয়, তবে এটি কেবল সূক্ষ্মভাবে আঁকা উচিত। আপনি যদি জেএসএন ব্যবহার করার পক্ষে জেদ করেন তবে আপনি jsonpickleমডিউলটি পরীক্ষা করতে পারেন যা একটি আকর্ষণীয় হাইব্রিড পদ্ধতির।

https://github.com/jsonpickle/jsonpickle


9
আমি আচার নিয়ে প্রধান সমস্যাটি হ'ল এটি পাইথন-নির্দিষ্ট ফর্ম্যাট, যখন জেএসএন প্ল্যাটফর্ম-স্বতন্ত্র ফর্ম্যাট। আপনি যদি কোনও মোবাইল অ্যাপ্লিকেশনের জন্য কোনও ওয়েব অ্যাপ্লিকেশন বা ব্যাকএন্ড লিখে থাকেন তবে জেএসএন বিশেষভাবে কার্যকর। যা বলা হয়েছে, jsonpickle নির্দেশ করার জন্য ধন্যবাদ।
হ্যারল্ডো_ওকে ২

@ হারল্ডো_ওকে কি জাসনপিকল এখনও জেএসএনে রফতানি করে না, কেবল খুব মানব পাঠযোগ্য নয়?
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

কীভাবে এটি করা শুরু করবেন সে সম্পর্কে কয়েকটি ভাল উত্তর রয়েছে। তবে কিছু বিষয় মনে রাখতে হবে:

  • উদাহরণটি যদি কোনও বৃহত ডেটা কাঠামোর ভিতরে বাসা বাঁধে তবে কী হবে?
  • ক্লাসের নামও যদি চাই?
  • আপনি যদি উদাহরণটি deserialize করতে চান?
  • আপনি যদি __slots__পরিবর্তে ব্যবহার করছেন __dict__?
  • আপনি যদি এটি নিজেই করতে চান না তবে কী হবে?

জসন-ট্রিকস একটি লাইব্রেরি (যা আমি তৈরি করেছি এবং অন্যরা এতে অবদান রেখেছিল) যা বেশ কিছু সময়ের জন্য এটি করতে সক্ষম হয়েছে। উদাহরণ স্বরূপ:

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

আমার জ্ঞানটি নিয়ে আমি যে সর্বোত্তম প্রচার করতে পারি তা হ'ল এটি।
মনে রাখবেন যে এই কোডটি ট্রিট সেট ()ও করে।
এই পদ্ধতির জেনারিক কেবল শ্রেণির সম্প্রসারণ প্রয়োজন (দ্বিতীয় উদাহরণে)।
নোট করুন যে আমি এটি কেবল ফাইলগুলিতে করছি তবে আপনার স্বাদে আচরণটি পরিবর্তন করা সহজ।

তবে এটি একটি কোডেক।

আরও কিছুটা কাজ করে আপনি নিজের ক্লাসটি অন্য উপায়ে তৈরি করতে পারেন। আমি এটির উদাহরণস্বরূপ একটি ডিফল্ট কনস্ট্রাক্টর ধরে নিই, আমি ক্লাস ডিক আপডেট করি।

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)

সম্পাদন করা

আরও কিছু গবেষণার সাহায্যে আমি একটি মেটাক্লাস ব্যবহার করে সুপারক্লাস রেজিস্টার পদ্ধতি কলের প্রয়োজন ছাড়াই সাধারণকরণের একটি উপায় পেয়েছি

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)

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.