วิธีการแปลงข้อมูล JSON เป็นวัตถุ Python


281

ฉันต้องการใช้ Python เพื่อแปลงข้อมูล JSON เป็นวัตถุ Python

ฉันได้รับวัตถุข้อมูล JSON จาก Facebook API ซึ่งฉันต้องการเก็บไว้ในฐานข้อมูลของฉัน

มุมมองปัจจุบันของฉันใน Django (Python) ( request.POSTมี JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • ทำงานได้ดี แต่ฉันจะจัดการกับวัตถุข้อมูล JSON ที่ซับซ้อนได้อย่างไร

  • จะดีกว่านี้ไหมถ้าฉันจะแปลงวัตถุ JSON นี้เป็นวัตถุ Python เพื่อให้ใช้งานได้ง่าย


โดยทั่วไป JSON จะถูกแปลงเป็นรายการวานิลลาหรือ dicts นั่นคือสิ่งที่คุณต้องการ? หรือคุณหวังที่จะแปลง JSON ให้ตรงกับประเภทที่กำหนดเอง?
Shakakai

ฉันต้องการแปลงเป็นวัตถุสิ่งที่ฉันสามารถเข้าถึงได้โดยใช้ "." . ชอบจากตัวอย่างด้านบน -> reponse.name, response.education.id ฯลฯ ....
Sai Krishna

44
การใช้dicts เป็นวิธีที่อ่อนแอในการเขียนโปรแกรมเชิงวัตถุ พจนานุกรมเป็นวิธีที่ไม่ดีในการสื่อสารความคาดหวังกับผู้อ่านรหัสของคุณ การใช้พจนานุกรมคุณสามารถระบุได้อย่างชัดเจนและนำมาใช้ซ้ำว่าต้องใช้คู่คีย์ - ค่าพจนานุกรมในขณะที่คนอื่นไม่ได้? สิ่งที่เกี่ยวกับการยืนยันว่าค่าที่กำหนดอยู่ในช่วงหรือชุดที่ยอมรับได้? ฟังก์ชั่นที่เฉพาะเจาะจงกับประเภทของวัตถุที่คุณกำลังทำงานด้วย (วิธี aka) คืออะไร? พจนานุกรมมีประโยชน์และใช้งานได้หลากหลาย แต่มี devs มากเกินไปที่ทำเหมือนว่าพวกเขาลืม Python เป็นภาษาเชิงวัตถุด้วยเหตุผล
Stew

1
มีห้องสมุดไพ ธ อนสำหรับgithub.com/jsonpickle/jsonpickleนี้(แสดงความคิดเห็นเนื่องจากคำตอบอยู่ด้านล่างมากเกินไปในเธรดและไม่สามารถเข้าถึงได้)
ความปรารถนาดี

คำตอบ:


355

คุณสามารถทำได้ในหนึ่งบรรทัดโดยใช้namedtupleและobject_hook:

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

หรือเพื่อนำสิ่งนี้กลับมาใช้ใหม่ได้ง่าย:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

หากคุณต้องการที่จะจัดการคีย์ที่มีชื่อแอตทริบิวต์ไม่ดีตรวจสอบnamedtupleของพารามิเตอร์rename


8
ซึ่งอาจส่งผลให้เกิดข้อผิดพลาดค่า ValueError: ชื่อประเภทและชื่อฟิลด์ไม่สามารถเริ่มต้นด้วยตัวเลข: '123'
PvdL

3
ในฐานะมือใหม่สำหรับ Python ฉันสนใจว่านี่เป็นสิ่งที่ประหยัดเช่นกันเมื่อความปลอดภัยเป็นปัญหา
ใจดี

8
สิ่งนี้สร้างคลาสใหม่ที่แตกต่างกันในแต่ละครั้งที่พบวัตถุ JSON ขณะแยกวิเคราะห์ใช่ไหม
fikr4n

2
น่าสนใจ ฉันคิดว่าการพึ่งพาd.keys()และการd.values()วนซ้ำในลำดับเดียวกันจะไม่รับประกัน แต่ฉันคิดผิด เอกสารกล่าวว่า: "ถ้าคีย์ค่าและมุมมองรายการจะซ้ำไปกับการปรับเปลี่ยนไม่แทรกแซงในพจนานุกรมคำสั่งของรายการที่จะสอดคล้องโดยตรง." เหมาะสำหรับบล็อกขนาดเล็กในพื้นที่ ฉันต้องการเพิ่มความคิดเห็นเพื่อแจ้งเตือนผู้ดูแลโค้ดที่ต้องพึ่งพาอย่างชัดเจน
cfi

1
ฉันไม่ได้ตระหนักถึงการดำเนินการย้อนกลับวัตถุประสงค์ทั่วไปที่ดีใด ๆ บุคคลที่มีชื่อว่า tuple สามารถเปลี่ยนเป็น dict โดยใช้x._asdict()ซึ่งอาจช่วยกรณีง่าย ๆ
DS

127

ตรวจสอบส่วนที่ชื่อเชี่ยวชาญ JSON วัตถุถอดรหัสในเอกสารโมดูลjson คุณสามารถใช้มันเพื่อถอดรหัสวัตถุ JSON เป็นชนิด Python ที่เจาะจง

นี่คือตัวอย่าง:

class User(object):
    def __init__(self, name, username):
        self.name = name
        self.username = username

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

ปรับปรุง

หากคุณต้องการเข้าถึงข้อมูลในพจนานุกรมผ่านโมดูล json ให้ทำสิ่งนี้:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

เหมือนพจนานุกรมทั่วไป


1
เฮ้ฉันเพิ่งอ่านและรู้ว่าพจนานุกรมจะทำทั้งหมดฉันเพียง แต่สงสัยว่าจะแปลงวัตถุ JSON เป็นพจนานุกรมได้อย่างไรและฉันจะเข้าถึงข้อมูลนี้จากพจนานุกรมได้อย่างไร
สายกฤษณะ

ยอดเยี่ยมมันเกือบจะชัดเจนแล้วอยากรู้อีกเล็กน้อยว่าถ้ามีวัตถุนี้ -> {'การศึกษา': {'name1': 456, 'name2': 567}} ฉันจะเข้าถึงข้อมูลนี้ได้อย่างไร
สายกฤษณะ

มันจะเป็น topLevelData ['education'] ['name1'] ==> 456 ใช่ไหม?
Shakakai

1
@Ben: ฉันคิดว่าความคิดเห็นของคุณไม่เหมาะสม จากคำตอบทั้งหมดที่นี่ในขณะนี้เป็นเพียงคนเดียวที่จะได้รับชั้นเรียนที่ถูกต้อง ซึ่งหมายความว่า: เป็นการดำเนินการหนึ่งรอบและผลลัพธ์ใช้ชนิดที่ถูกต้อง ดองเองสำหรับการใช้งานที่แตกต่างจาก JSON (ตัวแทนไบนารีเทียบกับต้นฉบับ) และ jsonpickle เป็น lib ที่ไม่ได้มาตรฐาน ฉันจะสนใจที่จะดูว่าคุณแก้ปัญหาที่ว่ามาตรฐาน JSON lib ไม่ได้ให้ต้นไม้แยกบนเพื่อเบ็ดวัตถุ
CFI

ฉันต้องเห็นด้วยกับ @Ben ในเรื่องนี้ นี่เป็นทางออกที่ไม่ดีจริงๆ ไม่สามารถปรับขนาดได้เลย คุณจะต้องรักษาชื่อฟิลด์เป็นสตริงและฟิลด์ หากคุณต้องการ refactor สาขาของคุณการถอดรหัสจะล้มเหลว (แน่นอนว่าข้อมูลที่ต่อเนื่องแล้วจะไม่เกี่ยวข้องอีกต่อไป) แนวคิดเดียวกันนี้ได้ถูกนำไปใช้กับjsonpickle แล้ว
guyarad

99

นี่ไม่ใช่โค้ดกอล์ฟ แต่นี่เป็นกลอุบายที่สั้นที่สุดของฉันโดยใช้types.SimpleNamespaceเป็นคอนเทนเนอร์สำหรับวัตถุ JSON

เมื่อเทียบกับnamedtupleโซลูชั่นชั้นนำมันคือ:

  • อาจเร็วกว่า / เล็กกว่าเนื่องจากไม่ได้สร้างคลาสสำหรับแต่ละวัตถุ
  • สั้น
  • ไม่มีrenameตัวเลือกและอาจเป็นข้อ จำกัด เดียวกันกับคีย์ที่ไม่ใช่ตัวระบุที่ถูกต้อง (ใช้setattrภายใต้หน้ากาก)

ตัวอย่าง:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)

2
โดยวิธีการที่ห้องสมุด serialization Marshmallow มีคุณสมบัติที่คล้ายกันกับ@post_loadมัณฑนากรของมัน marshmallow.readthedocs.io/en/latest/…
Taylor Edmiston

3
เพื่อหลีกเลี่ยงการพึ่งพา argparse: แทนที่การนำเข้า argparse ด้วยfrom types import SimpleNamespaceและใช้:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig

8
นี่คือทางออกที่ดีที่สุดควรอยู่ด้านบน
ScalaWilliam

4
แก้ไขเพื่อใช้โซลูชันของ @ maxschlepzig เมื่อทำงานภายใต้ Python 3.x ( types.SimpleNamespaceไม่มีใน 2.7 แต่น่าเสียดายที่)
Dan Lenski

1
ทำไมprint_function?
chwi

90

คุณสามารถลองสิ่งนี้:

class User(object):
    def __init__(self, name, username, *args, **kwargs):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

เพียงสร้างวัตถุใหม่แล้วส่งพารามิเตอร์เป็นแผนที่


1
ฉันได้รับ TypeError: วัตถุ 'ผู้ใช้' ไม่สามารถห้อยได้
มาห์ดี

1
นี่ควรเป็นคำตอบที่ยอมรับได้ โฆษณาสำหรับฉันทำงานได้ง่ายที่สุดกว่าที่เหลือทั้งหมด
Izik

ฉันไม่ได้ใช้ * args, ** kwargs แต่วิธีแก้ปัญหาใช้งานได้
Malkaviano

1
ผู้ใช้ (** j) บอกว่าไม่มีพารามิเตอร์ชื่อและชื่อผู้ใช้และวิธีการที่dictเริ่มต้นได้อย่างไร
Aaron Stainback

40

นี่คือทางเลือก json pickle ที่รวดเร็วและสกปรก

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

    def to_json(self):
        return json.dumps(self.__dict__)

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()

1
นี่ไม่ใช่วิธีการที่ดี ทีแรก to_json และ from_json ไม่ควรอยู่ในชั้นเรียนของคุณ อย่างที่สองมันจะไม่ทำงานสำหรับคลาสที่ซ้อนกัน
Jurass

17

สำหรับวัตถุที่ซับซ้อนคุณสามารถใช้JSON Pickle

Python library สำหรับซีเรียลไลซ์กราฟวัตถุใด ๆ ลงใน JSON สามารถใช้วัตถุ Python ใดก็ได้และเปลี่ยนวัตถุเป็น JSON นอกจากนี้มันสามารถสร้างวัตถุกลับเป็น Python ได้อีกครั้ง


6
ฉันคิดว่าjsonstructดีกว่า jsonstruct originally a fork of jsonpickle (Thanks guys!). The key difference between this library and jsonpickle is that during deserialization, jsonpickle requires Python types to be recorded as part of the JSON. This library intends to remove this requirement, instead, requires a class to be passed in as an argument so that its definition can be inspected. It will then return an instance of the given class. This approach is similar to how Jackson (of Java) works.
Abhishek Gupta

3
ปัญหาที่เกิดขึ้นกับ jsonstruct ก็คือว่ามันไม่ได้รับการรักษา (ในความเป็นจริงมันมีลักษณะที่ถูกทิ้งร้าง) '[{"name":"object1"},{"name":"object2"}]'และจะล้มเหลวในการแปลงรายชื่อของวัตถุเช่น jsonpickle จัดการได้ไม่ดีเช่นกัน
LS

1
ฉันไม่รู้ว่าทำไมคำตอบนี้ไม่ได้รับการโหวตมากขึ้น โซลูชันอื่น ๆ ส่วนใหญ่ค่อนข้างออกมี บางคนพัฒนาไลบรารีที่ยอดเยี่ยมสำหรับ JSON de / serialization - ทำไมไม่ใช้มันล่ะ นอกจากนี้ดูเหมือนว่าจะทำงานได้ดีกับรายการ - คุณมีปัญหาอะไรกับ @LS
guyarad

1
@guyarad ปัญหาคือ: x = jsonpickle.decode ('[{"ชื่อ": "object1"}, {"ชื่อ": "object2"}]') ให้รายการพจนานุกรม ([{'ชื่อ': ' object1 '}, {' name ':' object2 '}]), ไม่ใช่รายการของวัตถุที่มีคุณสมบัติ (x [0] .name ==' object1 ') ซึ่งเป็นคำถามเดิมที่ต้องการ เพื่อให้ได้สิ่งนั้นฉันลงเอยด้วยการใช้ object_hook / Namespace ที่แนะนำโดย eddygeek แต่วิธีการที่รวดเร็ว / สกปรกโดย ubershmekel ก็ดูดีเช่นกัน ฉันคิดว่าฉันสามารถใช้ object_hook กับ set_encoder_options ของ jsonpickle () (ไม่มีเอกสาร!) แต่มันจะใช้รหัสมากกว่าโมดูล json พื้นฐาน ฉันชอบที่จะพิสูจน์ว่าผิด!
LS

@LS หากคุณไม่สามารถควบคุมอินพุตได้ซึ่งเป็นสิ่งที่ OP ต้องการอย่างแท้จริง jsonpickle นั้นไม่เหมาะอย่างยิ่งเนื่องจากคาดว่าจะเป็นประเภทจริงในแต่ละระดับ (และจะถือว่าเป็นประเภทพื้นฐานหากไม่มี) วิธีแก้ปัญหาทั้งสองเป็น "น่ารัก"
guyarad

12

หากคุณใช้ Python 3.5+ คุณสามารถใช้jsonsเพื่อทำให้เป็นอนุกรมและดีซีเรียลไลซ์เป็นวัตถุ Python แบบเก่า:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

คุณสามารถFbApiUserสืบทอดจากjsons.JsonSerializableเพื่อความสง่างามมากขึ้น:

user = FbApiUser.from_json(response)

ตัวอย่างเหล่านี้จะใช้งานได้ถ้าคลาสของคุณประกอบด้วยชนิดเริ่มต้นของ Python เช่นสตริงจำนวนเต็มรายการชุดข้อมูล ฯลฯjsonslib จะต้องการคำแนะนำประเภทสำหรับประเภทที่กำหนดเอง


7

หากคุณกำลังใช้หลาม 3.6 ขึ้นไปคุณสามารถใช้ขนมหวาน-dataclass ตรงกันข้ามกับวิธีแก้ปัญหาทั้งหมดที่กล่าวมาข้างต้นเป็นวิธีที่ง่ายและปลอดภัย

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})

TypeError: make_data_class() got an unexpected keyword argument 'many'
จอห์น

@JOhn: คุณควรเปิดปัญหากับกรณีทดสอบที่ทำซ้ำได้ในgithub.com/lovasoa/marshmallow_dataclass/issues
lovasoa

6

การปรับปรุงคำตอบที่ดีมากของ lovasoa

หากคุณใช้ python 3.6+ คุณสามารถใช้:
pip install marshmallow-enumและ
pip install marshmallow-dataclass

มันง่ายและพิมพ์ปลอดภัย

คุณสามารถเปลี่ยนชั้นเรียนของคุณใน string-json และในทางกลับกัน:

จาก Object ถึง String Json:

    from marshmallow_dataclass import dataclass
    user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
    user_json = User.Schema().dumps(user)
    user_json_str = user_json.data

จาก String Json ถึง Object:

    json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
    user, err = User.Schema().loads(json_str)
    print(user,flush=True)

คำจำกัดความของคลาส:

class OrderStatus(Enum):
    CREATED = 'Created'
    PENDING = 'Pending'
    CONFIRMED = 'Confirmed'
    FAILED = 'Failed'

@dataclass
class User:
    def __init__(self, name, orderId, productName, quantity, status):
        self.name = name
        self.orderId = orderId
        self.productName = productName
        self.quantity = quantity
        self.status = status

    name: str
    orderId: str
    productName: str
    quantity: int
    status: OrderStatus

1
คุณไม่ต้องการคอนสตรัคเตอร์เพียงแค่ผ่าน init = True ไปยังดาต้าคลาสและคุณพร้อมที่จะไป
Josef Korbel

5

ฉันได้เขียนกรอบการทำให้เป็นอนุกรมขนาดเล็ก (de) ที่เรียกว่าany2anyที่ช่วยทำการแปลงที่ซับซ้อนระหว่าง Python สองประเภท

ในกรณีของคุณฉันคิดว่าคุณต้องการแปลงจากพจนานุกรม (รับด้วยjson.loads) เป็นวัตถุที่ซับซ้อนresponse.education ; response.nameพร้อมโครงสร้างซ้อนกันresponse.education.idเป็นต้น ... นั่นคือสิ่งที่เฟรมเวิร์กทำ เอกสารยังไม่ดี แต่โดยใช้any2any.simple.MappingToObjectคุณควรจะสามารถทำได้ง่ายมาก โปรดถามว่าคุณต้องการความช่วยเหลือหรือไม่


Sebpiq ได้ทำการติดตั้ง any2any และกำลังมีปัญหาในการทำความเข้าใจกับลำดับของการเรียกเมธอด คุณช่วยยกตัวอย่างการแปลงพจนานุกรมเป็นวัตถุ Python ด้วยคุณสมบัติสำหรับแต่ละคีย์ได้หรือไม่?
sansjoe

สวัสดี @sansjoe! หากคุณติดตั้งจาก pypi เวอร์ชันนั้นล้าสมัยไปแล้วฉันได้ทำการปรับโครงสร้างระบบใหม่เสร็จสมบูรณ์เมื่อไม่กี่สัปดาห์ที่ผ่านมา คุณควรใช้รุ่น GitHub (I ต้องการที่จะทำให้การปล่อยที่เหมาะสม!)
sebpiq

ฉันติดตั้งจาก pypy เพราะ github บอกว่าจะติดตั้งจาก pypy อีกอย่างคุณบอกว่า pypy ล้าสมัยไปแล้วหลายเดือนแล้ว .. มันใช้งานไม่ได้ :( ฉันยื่นรายงานข้อผิดพลาด tho! github.com/sebpiq/any2any/issues/11
sneilan

4

ตั้งแต่ไม่มีใครให้คำตอบเหมือนของฉันฉันจะโพสต์ที่นี่

เป็นคลาสที่แข็งแกร่งที่สามารถแปลงไปมาระหว่าง json strและdictฉันได้คัดลอกจากคำตอบของฉันไปยังคำถามอื่นอย่างง่ายดาย :

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

json_str = """... json string ..."""

py_json = PyJSON(json_str)

2

การแก้ไข @DS ตอบสนองเล็กน้อยเพื่อโหลดจากไฟล์:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def load_data(file_name):
  with open(file_name, 'r') as file_data:
    return file_data.read().replace('\n', '')
def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)

สิ่งหนึ่งที่: ไม่สามารถโหลดรายการที่มีตัวเลขอยู่ข้างหน้า แบบนี้:

{
  "1_first_item": {
    "A": "1",
    "B": "2"
  }
}

เนื่องจาก "1_first_item" ไม่ใช่ชื่อเขตข้อมูลของงูหลามที่ถูกต้อง


2

ในขณะที่ค้นหาวิธีการแก้ปัญหาฉันได้พบกับโพสต์บล็อกนี้: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

มันใช้เทคนิคเดียวกับที่ระบุไว้ในคำตอบก่อนหน้า แต่ด้วยการใช้งานของนักตกแต่ง อีกสิ่งหนึ่งที่ฉันพบว่ามีประโยชน์คือความจริงที่ว่ามันส่งคืนวัตถุที่พิมพ์เมื่อสิ้นสุดการดีซีเรียลไลเซชัน

class JsonConvert(object):
    class_mappings = {}

    @classmethod
    def class_mapper(cls, d):
        for keys, cls in clsself.mappings.items():
            if keys.issuperset(d.keys()):   # are all required arguments present?
                return cls(**d)
        else:
            # Raise exception instead of silently returning None
            raise ValueError('Unable to find a matching class for object: {!s}'.format(d))

    @classmethod
    def complex_handler(cls, Obj):
        if hasattr(Obj, '__dict__'):
            return Obj.__dict__
        else:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))

    @classmethod
    def register(cls, claz):
        clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
        return cls

    @classmethod
    def to_json(cls, obj):
        return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)

    @classmethod
    def from_json(cls, json_str):
        return json.loads(json_str, object_hook=cls.class_mapper)

การใช้งาน:

@JsonConvert.register
class Employee(object):
    def __init__(self, Name:int=None, Age:int=None):
        self.Name = Name
        self.Age = Age
        return

@JsonConvert.register
class Company(object):
    def __init__(self, Name:str="", Employees:[Employee]=None):
        self.Name = Name
        self.Employees = [] if Employees is None else Employees
        return

company = Company("Contonso")
company.Employees.append(Employee("Werner", 38))
company.Employees.append(Employee("Mary"))

as_json = JsonConvert.to_json(company)
from_json = JsonConvert.from_json(as_json)
as_json_from_json = JsonConvert.to_json(from_json)

assert(as_json_from_json == as_json)

print(as_json_from_json)

2

การขยายคำตอบของ DS ถ้าคุณต้องการให้อ็อบเจกต์ผันแปรได้ (ซึ่งไม่ได้ชื่อว่า named tuple) คุณสามารถใช้ไลบรารี่ไลบรารีคลาสแทนการตั้งชื่อว่า

import json
from recordclass import recordclass

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse into a mutable object
x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))

วัตถุที่ถูกดัดแปลงนั้นสามารถแปลงกลับเป็น json ได้อย่างง่ายดายมากโดยใช้simplejson :

x.name = "John Doe"
new_json = simplejson.dumps(x)

1

หากคุณใช้ Python 3.6 หรือใหม่กว่าคุณสามารถดูsquemaซึ่งเป็นโมดูลน้ำหนักเบาสำหรับโครงสร้างข้อมูลที่พิมพ์แบบคงที่ มันทำให้โค้ดของคุณอ่านง่ายในขณะเดียวกันก็ให้การตรวจสอบความถูกต้องของข้อมูลการแปลงและการทำให้เป็นอนุกรมโดยไม่ต้องทำงานพิเศษ คุณสามารถคิดว่ามันเป็นทางเลือกที่ซับซ้อนมากขึ้นและมีความคิดเห็นที่มีชื่อกับ tuples และ dataclasses นี่คือวิธีการใช้งาน:

from uuid import UUID
from squema import Squema


class FbApiUser(Squema):
    id: UUID
    age: int
    name: str

    def save(self):
        pass


user = FbApiUser(**json.loads(response))
user.save()

นี่ยังคล้ายกับวิธีภาษา JVM มากขึ้น
javadba

1

ฉันกำลังค้นหาวิธีแก้ปัญหาที่ทำงานร่วมกับ recordclass.RecordClassสนับสนุนวัตถุที่ซ้อนกันและใช้ได้กับทั้งการทำให้เป็นอนุกรม json และ deserialization json

การขยายคำตอบของ DS และการขยายวิธีแก้ไขปัญหาจาก BeneStr ฉันมาพร้อมกับสิ่งต่อไปนี้ที่ใช้งานได้:

รหัส:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

การใช้งาน:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

1

คำตอบที่ให้ไว้ที่นี่ไม่ส่งคืนชนิดของวัตถุที่ถูกต้องดังนั้นฉันจึงสร้างวิธีการเหล่านี้ด้านล่าง นอกจากนี้ยังล้มเหลวหากคุณพยายามเพิ่มเขตข้อมูลเพิ่มเติมในคลาสที่ไม่มีอยู่ใน JSON ที่กำหนด:

def dict_to_class(class_name: Any, dictionary: dict) -> Any:
    instance = class_name()
    for key in dictionary.keys():
        setattr(instance, key, dictionary[key])
    return instance


def json_to_class(class_name: Any, json_string: str) -> Any:
    dict_object = json.loads(json_string)
    return dict_to_class(class_name, dict_object)

0

Python3.x

สิ่งที่ดีที่สุดที่ฉันสามารถเข้าถึงได้ด้วยความรู้ของฉันคือสิ่งนี้
โปรดทราบว่ารหัสนี้ถือว่าชุด () เกินไป
วิธีการนี้เป็นวิธีการทั่วไปที่ต้องการการขยายชั้นเรียน (ในตัวอย่างที่สอง)
โปรดทราบว่าฉันแค่ทำมันกับไฟล์ แต่มันง่ายที่จะปรับเปลี่ยนพฤติกรรมตามรสนิยมของคุณ

อย่างไรก็ตามนี่คือ CoDec

ด้วยการทำงานเพิ่มอีกเล็กน้อยคุณสามารถสร้างชั้นเรียนของคุณในรูปแบบอื่นได้ ฉันถือว่าตัวสร้างเริ่มต้นให้เป็นอินสแตนซ์นั้นจากนั้นฉันอัพเดทคลาส dict

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โดยใช้metaclass

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

0

คุณสามารถใช้ได้

x = Map(json.loads(response))
x.__class__ = MyClass

ที่ไหน

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = Map(v)

        if kwargs:
            # for python 3 use kwargs.items()
            for k, v in kwargs.iteritems():
                self[k] = v
                if isinstance(v, dict):
                    self[k] = Map(v)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

สำหรับโซลูชันทั่วไปที่มีอนาคต


-5

ใช้jsonโมดูล ( ใหม่ใน Python 2.6 ) หรือsimplejsonโมดูลที่ติดตั้งเกือบตลอดเวลา


2
เฮ้ขอบคุณสำหรับการตอบกลับ คุณช่วยกรุณาโพสต์ตัวอย่างของวิธีการถอดรหัส JSON แล้วเข้าถึงข้อมูลนั้นได้หรือไม่?
สายกฤษณะ

เฮ้ตอนนี้คุณมีประเด็น แต่อย่างใดฉันชอบที่จะทำโดยไม่ทราบแล้วทำวิศวกรรมย้อนกลับ: D.
Sai Krishna

1
@Zach: มีตัวอย่างอยู่ที่ด้านบนสุดของเอกสารที่ฉันเชื่อมโยง
Chris Morgan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.