Ich habe die folgende Zeichenfolge
{"action":"print","method":"onData","data":"Madan Mohan"}
Ich möchte zu einem Objekt der Klasse deserialisieren
class payload
string action
string method
string data
Ich benutze Python 2.6 und 2.7
Antworten:
>>> j = '{"action": "print", "method": "onData", "data": "Madan Mohan"}' >>> import json >>> >>> class Payload(object): ... def __init__(self, j): ... self.__dict__ = json.loads(j) ... >>> p = Payload(j) >>> >>> p.action 'print' >>> p.method 'onData' >>> p.data 'Madan Mohan'
quelle
__dict__
so zu. Sie verlieren vorhandene Mitglieder, wenn sie nicht in der JSON-Zeichenfolge enthalten sind. Wenn Sie es tun müssen, verwenden Sieself.__dict__.update(otherdict)
Um Samis Antwort näher zu erläutern:
Aus den Dokumenten :
class Payload(object): def __init__(self, action, method, data): self.action = action self.method = method self.data = data import json def as_payload(dct): return Payload(dct['action'], dct['method'], dct['data']) payload = json.loads(message, object_hook = as_payload)
Mein Einwand gegen die
Die Lösung besteht darin, dass die Payload-Klasse, während sie die Arbeit erledigt und präzise ist, vollständig generisch wird - sie dokumentiert ihre Felder nicht.
Wenn die Payload-Nachricht beispielsweise ein unerwartetes Format hatte, wurde beim Erstellen der Payload kein Fehler generiert, anstatt einen nicht gefundenen Schlüsselfehler auszulösen, bis die Payload verwendet wurde.
quelle
return Payload(dct['action'], dct['method'], dct['data'])
mitreturn Payload(dct.get('action', None), dct.get('method', None), dct.get('data', None))
einigen Feldern zu ermöglichen , optional zu sein.Wenn Sie die Typhinweise in Python 3.6 übernehmen, können Sie dies folgendermaßen tun:
def from_json(data, cls): annotations: dict = cls.__annotations__ if hasattr(cls, '__annotations__') else None if issubclass(cls, List): list_type = cls.__args__[0] instance: list = list() for value in data: instance.append(from_json(value, list_type)) return instance elif issubclass(cls, Dict): key_type = cls.__args__[0] val_type = cls.__args__[1] instance: dict = dict() for key, value in data.items(): instance.update(from_json(key, key_type), from_json(value, val_type)) return instance else: instance : cls = cls() for name, value in data.items(): field_type = annotations.get(name) if inspect.isclass(field_type) and isinstance(value, (dict, tuple, list, set, frozenset)): setattr(instance, name, from_json(value, field_type)) else: setattr(instance, name, value) return instance
Damit können Sie typisierte Objekte wie folgt instanziieren:
class Bar: value : int class Foo: x : int bar : List[Bar] obj : Foo = from_json(json.loads('{"x": 123, "bar":[{"value": 3}, {"value": 2}, {"value": 1}]}'), Foo) print(obj.x) print(obj.bar[2].value)
Diese Syntax erfordert jedoch Python 3.6 und deckt nicht alle Fälle ab - zum Beispiel die Unterstützung der Eingabe. Alle ... Aber zumindest verschmutzt sie nicht die Klassen, die mit zusätzlichen init / tojson-Methoden deserialisiert werden müssen.
quelle
Wenn Sie Codezeilen speichern und die flexibelste Lösung beibehalten möchten, können wir die JSON-Zeichenfolge in ein dynamisches Objekt deserialisieren:
p = lambda:None p.__dict__ = json.loads('{"action": "print", "method": "onData", "data": "Madan Mohan"}')
>>>> p.action
output: u'print '
>>>> p.method
output: u'onData '
quelle
p.__dict__ = json.loads('{"parent":{"child":{"name":"henry"}}}')
erfordert immer noch den Zugriff auf ein Kind wie ein Diktat.Ich dachte, ich verliere alle Haare, um diese „Herausforderung“ zu lösen. Ich hatte folgende Probleme:
Ich habe eine Bibliothek namens gefunden
jsonpickle
die sich als sehr nützlich erwiesen hat.Installation:
Hier ist ein Codebeispiel zum Schreiben verschachtelter Objekte in eine Datei:
import jsonpickle class SubObject: def __init__(self, sub_name, sub_age): self.sub_name = sub_name self.sub_age = sub_age class TestClass: def __init__(self, name, age, sub_object): self.name = name self.age = age self.sub_object = sub_object john_junior = SubObject("John jr.", 2) john = TestClass("John", 21, john_junior) file_name = 'JohnWithSon' + '.json' john_string = jsonpickle.encode(john) with open(file_name, 'w') as fp: fp.write(john_string) john_from_file = open(file_name).read() test_class_2 = jsonpickle.decode(john_from_file) print(test_class_2.name) print(test_class_2.age) print(test_class_2.sub_object.sub_name)
Ausgabe:
John 21 John jr.
Webseite: http://jsonpickle.github.io/
Hoffe, es wird Ihre Zeit (und Haare) sparen.
quelle
Ich ziehe es vor, die Felder zu überprüfen, z. B. damit Sie Fehler abfangen können, z. B. wenn Sie einen ungültigen JSON erhalten, oder nicht den erwarteten JSON, also habe ich namedtuples verwendet:
from collections import namedtuple payload = namedtuple('payload', ['action', 'method', 'data']) def deserialize_payload(json): kwargs = dict([(field, json[field]) for field in payload._fields]) return payload(**kwargs)
Auf diese Weise erhalten Sie nette Fehler, wenn der JSON, den Sie analysieren, nicht mit dem übereinstimmt, was Sie analysieren möchten
>>> json = {"action":"print","method":"onData","data":"Madan Mohan"} >>> deserialize_payload(json) payload(action='print', method='onData', data='Madan Mohan') >>> badjson = {"error":"404","info":"page not found"} >>> deserialize_payload(badjson) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in deserialize_payload KeyError: 'action'
Wenn Sie verschachtelte Relationen analysieren möchten,
'{"parent":{"child":{"name":"henry"}}}'
können Sie z. B. weiterhin die Namenstupel und sogar eine wiederverwendbarere Funktion verwendenPerson = namedtuple("Person", ['parent']) Parent = namedtuple("Parent", ['child']) Child = namedtuple('Child', ['name']) def deserialize_json_to_namedtuple(json, namedtuple): return namedtuple(**dict([(field, json[field]) for field in namedtuple._fields])) def deserialize_person(json): json['parent']['child'] = deserialize_json_to_namedtuple(json['parent']['child'], Child) json['parent'] = deserialize_json_to_namedtuple(json['parent'], Parent) person = deserialize_json_to_namedtuple(json, Person) return person
dir geben
>>> deserialize_person({"parent":{"child":{"name":"henry"}}}) Person(parent=Parent(child=Child(name='henry'))) >>> deserialize_person({"error":"404","info":"page not found"}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in deserialize_person KeyError: 'parent'
quelle
In neueren Versionen von Python können Sie die Marshmallow-Datenklasse verwenden :
from marshmallow_dataclass import dataclass @dataclass class Payload action:str method:str data:str Payload.Schema().load({"action":"print","method":"onData","data":"Madan Mohan"})
quelle
Sie können einen Encoder für die Objekterstellung spezialisieren: http://docs.python.org/2/library/json.html
import json class ComplexEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, complex): return {"real": obj.real, "imag": obj.imag, "__class__": "complex"} return json.JSONEncoder.default(self, obj) print json.dumps(2 + 1j, cls=ComplexEncoder)
quelle
Eine andere Möglichkeit besteht darin, die JSON-Zeichenfolge einfach als Diktat an den Konstruktor Ihres Objekts zu übergeben. Zum Beispiel ist Ihr Objekt:
class Payload(object): def __init__(self, action, method, data, *args, **kwargs): self.action = action self.method = method self.data = data
Und die folgenden zwei Zeilen Python-Code werden es konstruieren:
Grundsätzlich erstellen wir zuerst ein generisches JSON-Objekt aus der JSON-Zeichenfolge. Dann übergeben wir das generische json-Objekt als Diktat an den Konstruktor der Payload-Klasse. Der Konstruktor der Payload-Klasse interpretiert das Diktat als Schlüsselwortargumente und legt alle entsprechenden Felder fest.
quelle
Es gibt verschiedene Methoden, um JSON-Zeichenfolgen für ein Objekt zu deserialisieren. Alle oben genannten Methoden sind akzeptabel, aber ich empfehle die Verwendung einer Bibliothek, um doppelte Schlüsselprobleme oder das Serialisieren / Deserialisieren verschachtelter Objekte zu verhindern.
Pykson ist ein JSON Serializer und Deserializer für Python, mit denen Sie dies erreichen können. Definieren Sie einfach das Payload-Klassenmodell als JsonObject und konvertieren Sie die Pyson-Zeichenfolge mit Pykson in ein Objekt.
from pykson import Pykson, JsonObject, StringField class Payload(pykson.JsonObject): action = StringField() method = StringField() data = StringField() json_text = '{"action":"print","method":"onData","data":"Madan Mohan"}' payload = Pykson.from_json(json_text, Payload)
quelle
pydantic ist eine zunehmend beliebte Bibliothek für Python 3.6+ -Projekte. Es führt hauptsächlich Datenvalidierung und Einstellungsverwaltung mithilfe von Typhinweisen durch.
Ein einfaches Beispiel mit verschiedenen Typen:
from pydantic import BaseModel class ClassicBar(BaseModel): count_drinks: int is_open: bool data = {'count_drinks': '226', 'is_open': 'False'} cb = ClassicBar(**data) >>> cb ClassicBar(count_drinks=226, is_open=False)
Was ich an der Bibliothek liebe, ist, dass man viele Goodies kostenlos bekommt, wie zum Beispiel
>>> cb.json() '{"count_drinks": 226, "is_open": false}' >>> cb.dict() {'count_drinks': 226, 'is_open': False}
quelle
Während Alex ' Antwort auf eine gute Technik hinweist, stößt die von ihm angegebene Implementierung auf ein Problem, wenn wir verschachtelte Objekte haben.
class more_info string status class payload string action string method string data class more_info
mit dem folgenden Code:
def as_more_info(dct): return MoreInfo(dct['status']) def as_payload(dct): return Payload(dct['action'], dct['method'], dct['data'], as_more_info(dct['more_info'])) payload = json.loads(message, object_hook = as_payload)
payload.more_info
wird auch als eine Instanz behandelt,payload
die zu Analysefehlern führt.Aus den offiziellen Dokumenten:
Daher würde ich lieber die folgende Lösung vorschlagen:
class MoreInfo(object): def __init__(self, status): self.status = status @staticmethod def fromJson(mapping): if mapping is None: return None return MoreInfo( mapping.get('status') ) class Payload(object): def __init__(self, action, method, data, more_info): self.action = action self.method = method self.data = data self.more_info = more_info @staticmethod def fromJson(mapping): if mapping is None: return None return Payload( mapping.get('action'), mapping.get('method'), mapping.get('data'), MoreInfo.fromJson(mapping.get('more_info')) ) import json def toJson(obj, **kwargs): return json.dumps(obj, default=lambda j: j.__dict__, **kwargs) def fromJson(msg, cls, **kwargs): return cls.fromJson(json.loads(msg, **kwargs)) info = MoreInfo('ok') payload = Payload('print', 'onData', 'better_solution', info) pl_json = toJson(payload) l1 = fromJson(pl_json, Payload)
quelle