Chcę użyć języka Python do konwersji danych JSON na obiekt języka Python.
Otrzymuję obiekty danych JSON z interfejsu API Facebooka, które chcę przechowywać w mojej bazie danych.
Mój obecny widok w Django (Python) ( request.POST
zawiera JSON):
response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
Działa to dobrze, ale jak obsługiwać złożone obiekty danych JSON?
Czy nie byłoby znacznie lepiej, gdybym mógł jakoś przekonwertować ten obiekt JSON na obiekt Python dla łatwego użycia?
dict
s to słaby sposób na programowanie obiektowe. Słowniki to bardzo słaby sposób komunikowania oczekiwań czytelnikom twojego kodu. Używając słownika, w jaki sposób możesz jasno i wielokrotnie określić, że niektóre pary kluczy słownik-wartość są wymagane, a inne nie? Co powiesz na potwierdzenie, że dana wartość mieści się w dopuszczalnym zakresie lub jest ustawiona? Co z funkcjami specyficznymi dla typu obiektu, z którym pracujesz (inaczej metody)? Słowniki są przydatne i wszechstronne, ale zbyt wielu programistów zachowuje się tak, jakby zapomniało, że Python jest językiem zorientowanym obiektowo z jakiegoś powodu.Odpowiedzi:
Możesz to zrobić w jednym wierszu, używając
namedtuple
iobject_hook
:lub, aby ponownie łatwo to wykorzystać:
Jeśli chcesz go obsługiwać klawisze, które nie są dobre nazwy atrybutów, sprawdź
namedtuple
„srename
parametr .źródło
d.keys()
id.values()
powtarzanie w tej samej kolejności nie jest gwarantowane, ale się myliłem. W docs powiedzieć: „Jeśli klucze, wartości i poglądy są przedmioty powtórzyć nad bez interweniujących modyfikacji do słownika, kolejność rzeczy bezpośrednio odpowiadają.”. Dobrze wiedzieć o tak małych lokalnych blokach kodu. Dodałbym jednak komentarz, aby wyraźnie powiadomić opiekunów kodu o takiej zależności.x._asdict()
, co może pomóc w prostych przypadkach.Sprawdź sekcję Specjalizowanie dekodowania obiektów JSON w
json
dokumentacji modułu . Możesz go użyć do zdekodowania obiektu JSON na określony typ Pythona.Oto przykład:
Aktualizacja
Jeśli chcesz uzyskać dostęp do danych w słowniku za pomocą modułu json:
Podobnie jak zwykły słownik.
źródło
To nie jest kod golfowy, ale oto moja najkrótsza sztuczka, wykorzystująca
types.SimpleNamespace
jako pojemnik na obiekty JSON.W porównaniu do wiodącego
namedtuple
rozwiązania jest to:rename
opcji i prawdopodobnie takie same ograniczenie dotyczące kluczy, które nie są prawidłowymi identyfikatorami (zastosowaniasetattr
pod osłonami)Przykład:
źródło
@post_load
dekoratorem. marshmallow.readthedocs.io/en/latest/…from types import SimpleNamespace
x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
types.SimpleNamespace
niestety nie istnieje w 2.7).print_function
?Możesz spróbować:
Wystarczy utworzyć nowy obiekt i przekazać parametry jako mapę.
źródło
Oto szybka i brudna alternatywa dla marynaty
źródło
W przypadku złożonych obiektów możesz użyć JSON Pickle
źródło
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.
'[{"name":"object1"},{"name":"object2"}]'
. jsonpickle też nie radzi sobie z tym dobrze.Jeśli używasz Python 3.5+, możesz użyć
jsons
do serializacji i deserializacji do zwykłych starych obiektów Python:Możesz także
FbApiUser
dziedziczyćjsons.JsonSerializable
po więcej elegancji:Te przykłady będą działać, jeśli twoja klasa składa się z domyślnych typów języka Python, takich jak ciągi, liczby całkowite, listy, czasy danych itp. Biblioteka
jsons
będzie jednak wymagać wskazówek typów dla typów niestandardowych.źródło
Jeśli używasz Pythona 3.6+, możesz użyć marshmallow-dataclass . W przeciwieństwie do wszystkich wyżej wymienionych rozwiązań, jest on zarówno prosty, jak i bezpieczny:
źródło
TypeError: make_data_class() got an unexpected keyword argument 'many'
Poprawa bardzo dobrej odpowiedzi lovasoa.
Jeśli używasz Pythona 3.6+, możesz użyć:
pip install marshmallow-enum
ipip install marshmallow-dataclass
Jest prosty i bezpieczny dla typu.
Możesz przekształcić swoją klasę w string-json i odwrotnie:
Od obiektu do ciągu Json:
Od String Json do Object:
Definicje klas:
źródło
Napisałem małą strukturę (de) serializacji o nazwie any2any która pomaga w złożonej transformacji między dwoma typami Pythona.
W twoim przypadku myślę, że chcesz przekształcić ze słownika (uzyskanego za pomocą
json.loads
) w złożony obiektresponse.education ; response.name
, z zagnieżdżoną strukturąresponse.education.id
itp. Więc właśnie do tego służy ten framework. Dokumentacja nie jest jeszcze świetna, ale korzystając z niejany2any.simple.MappingToObject
, powinieneś być w stanie to zrobić bardzo łatwo. Zapytaj, czy potrzebujesz pomocy.źródło
Ponieważ nikt nie udzielił odpowiedzi podobnej do mojej, zamieszczę ją tutaj.
Jest to solidna klasa, która może z łatwością konwertować między jsonem
str
idict
którą skopiowałem z mojej odpowiedzi na inne pytanie :źródło
Trochę modyfikując odpowiedź @DS, aby załadować z pliku:
Jedno: nie można załadować przedmiotów z numerami z przodu. Lubię to:
Ponieważ „1_first_item” nie jest prawidłową nazwą pola python.
źródło
Podczas szukania rozwiązania natknąłem się na ten post na blogu: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/
Wykorzystuje tę samą technikę, jak podano w poprzednich odpowiedziach, ale z wykorzystaniem dekoratorów. Inną rzeczą, którą uznałem za przydatną, jest fakt, że zwraca on wpisany obiekt na końcu deserializacji
Stosowanie:
źródło
Rozwijając nieco odpowiedź DS, jeśli potrzebujesz, aby obiekt był modyfikowalny (którego nazwa nie ma), możesz użyć biblioteki klas rekordów zamiast namedtuple:
Zmodyfikowany obiekt można następnie bardzo łatwo przekonwertować z powrotem do formatu json za pomocą simplejson :
źródło
Jeśli używasz języka Python 3.6 lub nowszego, możesz rzucić okiem na squema - lekki moduł do statycznie typowanych struktur danych. Dzięki temu kod jest łatwy do odczytania, a jednocześnie zapewnia łatwą weryfikację, konwersję i serializację danych bez dodatkowej pracy. Możesz myśleć o tym jako o bardziej wyrafinowanej i upartej alternatywie dla nazwanych świątyń i klas danych. Oto jak możesz go użyć:
źródło
Szukałem rozwiązania, które działałoby
recordclass.RecordClass
, obsługuje zagnieżdżone obiekty i działa zarówno dla serializacji json, jak i deserializacji json.Rozwijając odpowiedź DS i rozwijając rozwiązanie BeneStr, opracowałem następujące rozwiązanie, które wydaje się działać:
Kod:
Stosowanie:
źródło
Podane tutaj odpowiedzi nie zwracają poprawnego typu obiektu, dlatego stworzyłem te metody poniżej. Nie powiodą się również, jeśli spróbujesz dodać więcej pól do klasy, która nie istnieje w danym JSON:
źródło
Python3.x
Najlepsze podejście, jakie mogłem osiągnąć dzięki mojej wiedzy, było takie.
Zauważ, że ten kod również traktuje set ().
To podejście jest ogólne, wymaga jedynie rozszerzenia klasy (w drugim przykładzie).
Zauważ, że robię to tylko dla plików, ale łatwo jest zmodyfikować zachowanie według własnego uznania.
Jest to jednak CoDec.
Przy odrobinie pracy możesz zbudować swoją klasę na inne sposoby. Zakładam, że domyślny konstruktor to instancja, a następnie aktualizuję dyktę klasową.
Edytować
Po kilku dalszych badaniach znalazłem sposób na uogólnienie bez potrzeby wywoływania metody rejestrowania SUPERCLASS za pomocą metaklasy
źródło
Możesz użyć
gdzie
Dla ogólnego, przyszłościowego rozwiązania.
źródło
Użyj
json
modułu ( nowego w Pythonie 2.6 ) lubsimplejson
modułu, który prawie zawsze jest zainstalowany.źródło