Używam Pythona 2 do analizy JSON z plików tekstowych zakodowanych w ASCII .
Podczas ładowania tych plików za pomocą json
lub simplejson
, wszystkie moje ciągi znaków są rzutowane na obiekty Unicode zamiast na ciągi znaków. Problem polega na tym, że muszę używać danych z niektórymi bibliotekami, które akceptują tylko obiekty łańcuchowe. Nie mogę zmienić bibliotek ani zaktualizować ich.
Czy można uzyskać obiekty łańcuchowe zamiast Unicode?
Przykład
>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b'] # I want these to be of type `str`, not `unicode`
Aktualizacja
To pytanie zostało zadane dawno temu , kiedy utknąłem w Pythonie 2 . Jednym łatwym i czystym rozwiązaniem na dziś jest użycie najnowszej wersji Pythona - tj. Python 3 i nowszych .
python
json
serialization
unicode
python-2.x
Brutus
źródło
źródło
str
Odpowiedzi:
Rozwiązanie z
object_hook
Przykładowe użycie:
Jak to działa i dlaczego miałbym go używać?
Funkcja Marka Amery jest krótsza i bardziej przejrzysta niż te, więc jaki jest ich sens? Dlaczego chcesz z nich korzystać?
Wyłącznie dla wydajności . Odpowiedź Marka dekoduje najpierw tekst JSON za pomocą ciągów Unicode, a następnie przechodzi przez całą zdekodowaną wartość, aby przekonwertować wszystkie ciągi na ciągi bajtowe. Ma to kilka niepożądanych efektów:
Ta odpowiedź łagodzi oba te problemy z wydajnością przy użyciu
object_hook
parametrujson.load
ijson.loads
. Z dokumentów :Ponieważ słowniki zagnieżdżone na wielu poziomach głęboko w innych słownikach są przekazywane
object_hook
podczas dekodowania , możemy w tym miejscu bajtować wszelkie ciągi znaków lub listy i unikać późniejszej potrzeby głębokiej rekurencji.Odpowiedź Marka nie nadaje się do użycia w
object_hook
obecnym kształcie, ponieważ powtarza się w zagnieżdżonych słownikach. Zapobiegamy tej rekurencji w tej odpowiedzi zignore_dicts
parametrem do_byteify
, który jest do niej przekazywany przez cały czas, z wyjątkiem sytuacji, gdyobject_hook
przekazuje ją do nowegodict
bajtowania.ignore_dicts
Flag opowiada_byteify
ignorowaćdict
s ponieważ już byteified.Wreszcie, nasze implementacje
json_load_byteified
ijson_loads_byteified
wywołanie_byteify
(zignore_dicts=True
) wyniku zwróconego zjson.load
lub wjson.loads
celu obsługi przypadku, w którym dekodowany tekst JSON nie madict
najwyższego poziomu.źródło
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
zreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
, aby działała.json_loads_byteified('[' * 990 + ']' * 990)
. Z 991 ulega awarii. Mark nadal współpracuje z 991:byteify(json.loads('[' * 991 + ']' * 991))
. Rozbija się przy 992. Tak więc przynajmniej w tym teście Mark może pójść głębiej, w przeciwieństwie do tego, co powiedziałeś.Chociaż jest tu kilka dobrych odpowiedzi, ostatecznie użyłem PyYAML do parsowania moich plików JSON, ponieważ daje klucze i wartości jako
str
ciągi znaków zamiastunicode
typu. Ponieważ JSON jest podzbiorem YAML, działa dobrze:Notatki
Należy jednak pamiętać o kilku rzeczach:
Dostaję obiekty łańcuchowe, ponieważ wszystkie moje wpisy są zakodowane w ASCII . Gdybym używał wpisów kodowanych w Unicode, odzyskałbym je jako obiekty Unicode - nie ma konwersji!
Powinieneś (prawdopodobnie zawsze) użyć
safe_load
funkcji PyYAML ; jeśli użyjesz go do ładowania plików JSON, i tak nie potrzebujesz „dodatkowej mocy”load
funkcji.Jeśli chcesz parser YAML, który ma większe wsparcie dla wersji 1.2 specyfikacji (i poprawnie analizuje bardzo niskie liczby ) wypróbuj Ruamel YAML :
pip install ruamel.yaml
i toimport ruamel.yaml as yaml
było wszystko, czego potrzebowałem w moich testach.Konwersja
Jak wspomniano, nie ma konwersji! Jeśli nie możesz być pewien, że zajmujesz się tylko wartościami ASCII (i nie masz pewności przez większość czasu), lepiej użyj funkcji konwersji :
Kilka razy korzystałem z tego od Marka Amery , działa świetnie i jest bardzo łatwy w użyciu. Zamiast tego możesz również użyć podobnej funkcji
object_hook
, ponieważ może ona zwiększyć wydajność dużych plików. Zobacz na to nieco bardziej zaangażowaną odpowiedź Mireca Miskufa .źródło
yaml.load(json.dumps([u'a', u'£', u'É']))
w powłoce Pythona i zauważ, że odzyskujesz['a', u'\xa3', u'\xc9']
(który zawieraunicode
ciągi znaków). Jeśli nie możesz być pewien, że twoje dane zawierają tylko znaki z zestawu znaków ASCII, powinieneś zastosować inne podejście (polecam własną odpowiedź).[u'a', u'b']
ostrożności.Nie ma wbudowanej opcji powodującej, że funkcje modułu json zwracają ciągi bajtów zamiast ciągów Unicode. Jednak ta krótka i prosta funkcja rekurencyjna przekształci dowolny zdekodowany obiekt JSON z użycia ciągów Unicode w ciągi bajtów zakodowanych w UTF-8:
Po prostu wywołaj to na wyjściu otrzymanym z połączenia
json.load
lubjson.loads
.Kilka uwag:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
sięreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, od słownika Ułatwienia nie były obsługiwane, aż Pythonie 2.7.object_hook
lubobject_pairs_hook
. Odpowiedź Mireca Miskufa jest jak dotąd jedyną, która potrafi to poprawnie wykonać, choć w konsekwencji jest znacznie bardziej skomplikowana niż moje podejście.źródło
object_hook
jest w rzeczywistości znacznie gorsza niż ta, ale przy użyciuobject_pairs_hook
można uzyskać dość wydajną metodę, która nie wymaga rekurencji ani ponownej wizyty węzłów, które nie zawierają ciągów.object_pairs_hook
Metoda jest chyba bardzo nieznacznie trudniejszy do zrozumienia niż ten jeden (trzeba zrozumieć, jak działa i dlaczego listy parametrów i dicts wymagają innego traktowania), a korzyści wydajność nie sprawa dla większości ludzi ... ale bym się spodziewać istnieje, szczególnie dla każdego, kto ma do czynienia z niezwykle głęboko zagnieżdżonym obiektem JSON.Możesz użyć
object_hook
parametru,json.loads
aby przekazać konwerter. Po fakcie nie trzeba wykonywać konwersji.json
Moduł będzie zawsze przechodząobject_hook
dicts tylko, a będzie to rekurencyjnie przechodzić w zagnieżdżonych dicts, więc nie musisz rekursja do zagnieżdżonych dicts siebie. Nie sądzę, bym konwertował ciągi znaków Unicode na liczby takie jak programy Wellsa. Jeśli jest to ciąg znaków Unicode, został zacytowany jako ciąg znaków w pliku JSON, więc powinien to być ciąg znaków (lub plik jest zły).Ponadto starałbym się unikać robienia czegoś takiego jak
str(val)
naunicode
obiekcie. Powinieneś używaćvalue.encode(encoding)
z prawidłowym kodowaniem, w zależności od tego, czego oczekuje twoja biblioteka zewnętrzna.Na przykład:
źródło
s
jest JSONObject
(nieuporządkowany zbiór klucz: wartość paruje się ze znakiem „:” oddzielającym klucz od wartości, oddzielony przecinkami i ujęty w nawiasy klamrowe), ale nie, jeśli, powiedzmy, JSONArray
. Więc jeśli otrzymamy JSONArray
jak["a", "b"]
, wynik nadal będzie[u'a', u'b']
. Żaden z innych obecnie dostępnych dostosowywania parametrów typu zaczepujson.loads()
nie może wykonać zadania.json
moduł będzie rekurencyjnie przekazywał w zagnieżdżonedict
s, nie ma potrzeby sprawdzania ich w dwóch funkcjach - więc dwieelif
klauzule, które je sprawdzają, powinny zostać usunięte.from Utility import *
, funkcje nie będą widoczne z powodu tego podkreślenia.object_hook
jest wywoływany dla każdego przeanalizowanego obiektu json, więc jeśli powrócisz do tego, co zostało ci dane, ponownie „utwierdzasz” rzeczy, które już „utwierdziłeś”. Wydajność wzrośnie geometrycznie wraz z rozmiarem obiektu. Podałem tutaj odpowiedź , która wykorzystujeobject_pairs_hook
i nie cierpi z powodu tego problemu.Jest tak, ponieważ json nie ma różnicy między obiektami łańcuchowymi a obiektami Unicode. Wszystkie są łańcuchami w javascript.
Myślę, że JSON ma rację zwracając obiekty Unicode . W rzeczywistości nie zaakceptowałbym niczego mniejszego, ponieważ ciągi javascript są w rzeczywistości
unicode
obiektami (tzn. Ciągi JSON (javascript) mogą przechowywać dowolny rodzaj znaku Unicode), więc sensowne jest tworzenieunicode
obiektów podczas tłumaczenia ciągów z JSON. Zwykłe ciągi po prostu nie pasowałyby, ponieważ biblioteka musiałaby odgadnąć kodowanie, jakie chcesz.Lepiej jest używać
unicode
obiektów łańcuchowych wszędzie. Dlatego najlepszą opcją jest aktualizacja bibliotek, aby mogły one obsługiwać obiekty Unicode.Ale jeśli naprawdę chcesz bajtowania, po prostu zakoduj wyniki do wybranego kodowania:
źródło
Istnieje łatwe obejście problemu.
TL; DR - użyj
ast.literal_eval()
zamiastjson.loads()
. Zarównoast
ijson
są w bibliotece standardowej.Chociaż nie jest to „idealna” odpowiedź, jest ona dość daleko, jeśli twoim planem jest całkowite zignorowanie Unicode. W Pythonie 2.7
daje:
Robi się bardziej włochaty, gdy niektóre obiekty są tak naprawdę łańcuchami Unicode. Pełna odpowiedź szybko staje się owłosiona.
źródło
null
,true
anifalse
wartości, ponieważ nie są one ważne w Pythonie i spowodujeliteral_eval()
niepowodzenie.\/
) wewnątrz łańcucha ani sekwencji ucieczki Unicode (jak"\u0061"
, co jest innym sposobem pisania"a"
). Dosłowna składnia Pythona jest niezgodna z JSON na kilka sposobów i nie ufałbym tej odpowiedzi dla żadnego skryptu, którego nie zamierzałem wyrzucić.json
do zrzucania danych, po prostu użyj,print
jeśli uruchomisz Pythona. Potemast.literal_eval
działaOdpowiedź Mike'a Brennana jest bliska, ale nie ma powodu, aby przemierzać całą strukturę. Jeśli używasz parametru
object_hook_pairs
(Python 2.7+):Za jego pomocą otrzymujesz każdy obiekt JSON, dzięki czemu możesz dekodować bez potrzeby rekurencji:
Zauważ, że nigdy nie muszę wywoływać haka rekurencyjnie, ponieważ każdy obiekt zostanie przekazany do haka, gdy go użyjesz
object_pairs_hook
. Musisz dbać o listy, ale jak widać, obiekt na liście zostanie poprawnie przekonwertowany i nie musisz się powtarzać, aby tak się stało.EDYCJA: Współpracownik zauważył, że Python2.6 nie ma
object_hook_pairs
. Nadal możesz tego używać w Pythonie 2.6, dokonując bardzo małej zmiany. W haku powyżej zmień:do
Następnie użyj
object_hook
zamiastobject_pairs_hook
:Wykorzystanie
object_pairs_hook
wyników powoduje utworzenie instancji jednego mniejszego słownika dla każdego obiektu w obiekcie JSON, co może być przydatne, jeśli analizujesz ogromny dokument.źródło
deunicodify_hook
, co wyświetlasz w tej odpowiedzi? W tej chwili masz implementacjędeunicodify_hook
, która nie dokonuje iteracji po listach i deunicodify napisów i list w nich zawartych, a zatem wyniki, które wyświetlasz, nie pasują do wyników, które faktycznie wygeneruje hak. Napraw to, a ta odpowiedź będzie lepsza od mojej.object_pairs_hook
wywoływane są tylko obiekty , jeśli tekst JSON zawiera listę ciągów na najwyższym poziomie, to rozwiązanie zakończy się niepowodzeniem. Nie ma sposobu, aby to naprawić bez wywołania jakiejś funkcji dla rzeczy zwróconejjson.load
; żaden zjson.load
haczyków nie gwarantuje, że poradzisz sobie z każdym sznurkiem. Myślę, że jest to na tyle duża wada, że wciąż zalecam moje rozwiązanie zamiast używania haków.Obawiam się, że nie ma możliwości automatycznego osiągnięcia tego celu w bibliotece simplejson.
Skaner i dekoder w simplejson są zaprojektowane do generowania tekstu Unicode. Aby to zrobić, biblioteka używa funkcji o nazwie
c_scanstring
(jeśli jest dostępna, dla szybkości) lubpy_scanstring
jeśli wersja C nie jest dostępna.scanstring
Funkcja nazywa się kilka razy przez prawie każdej rutyny że simplejson ma do dekodowania strukturę, która może zawierać tekst. Będziesz musiał alboscanstring
wpisać małpą wartość w simplejson.decoder, albo podklasęJSONDecoder
i podać prawie całą własną implementację wszystkiego, co może zawierać tekst.Powodem, dla którego simplejson wypisuje kod Unicode, jest jednak to, że specyfikacja json wyraźnie wspomina, że „Łańcuch jest zbiorem zerowych lub więcej znaków Unicode” ... zakłada się, że obsługa Unicode jest częścią samego formatu. Implementacja Simplejsona
scanstring
posuwa się tak daleko, że skanuje i interpretuje zmiany znaczenia Unicode (nawet sprawdzanie błędów w przypadku zniekształconych reprezentacji wielobajtowych zestawów znaków), więc jedynym sposobem, w jaki można niezawodnie zwrócić ci wartość, jest unicode.Jeśli masz starzejącą się bibliotekę, która tego potrzebuje
str
, polecam ci żmudne przeszukanie zagnieżdżonej struktury danych po parsowaniu (co, jak mówię, jest tym, co wyraźnie powiedziałeś, że chcesz uniknąć ... przepraszam), lub może zapakuj swoje biblioteki w coś w rodzaju fasada, w której można masować parametry wejściowe na bardziej szczegółowym poziomie. Drugie podejście może być łatwiejsze do zarządzania niż pierwsze, jeśli struktury danych są rzeczywiście głęboko zagnieżdżone.źródło
Jak słusznie zauważa Mark (Amery): Używanie deserializatora PyYamla na zrzutie jsona działa tylko, jeśli masz tylko ASCII. Przynajmniej po wyjęciu z pudełka.
Dwa szybkie komentarze na temat podejścia PyYaml:
NIGDY nie używaj yaml.load do danych z pola. Jest to funkcja (!) Programu yaml do wykonywania dowolnego kodu ukrytego w strukturze.
Państwo może zrobić to działa również za brak ASCII przez to:
Ale pod względem wydajności nie ma porównania z odpowiedzią Marka Amery:
Rzucając głęboko zagnieżdżone przykładowe dykty na dwie metody, otrzymuję to (z dt [j] = delta czasowa json.loads (json.dumps (m))):
Tak więc deserializacja, w tym pełne chodzenie po drzewie i kodowanie, w zakresie wielkości implementacji opartej na C. Uważam to za niezwykle szybkie i również bardziej wytrzymałe niż obciążenie yaml w głęboko zagnieżdżonych strukturach. I mniej podatne na błędy bezpieczeństwa, patrząc na yaml.load.
=> Chociaż doceniłbym wskaźnik do konwertera opartego tylko na C, funkcja bajtowania powinna być odpowiedzią domyślną.
Jest to szczególnie ważne, jeśli twoja struktura json pochodzi z pola zawierającego dane wprowadzone przez użytkownika. Bo wtedy prawdopodobnie trzeba chodzić w każdym razie nad strukturą - niezależnego od żądanej wewnętrznych struktur danych (unicode „kanapkę” lub ciągów bajtów tylko).
Czemu?
Normalizacja Unicode . Dla nieświadomych: weź środek przeciwbólowy i przeczytaj to .
Tak więc za pomocą rekurencji bajtowej zabijasz dwa ptaki jednym kamieniem:
W moich testach okazało się, że zastąpienie input.encode ('utf-8') unicodedata.normalize ('NFC', wejście) .encode ('utf-8') było nawet szybsze niż bez NFC - ale to w dużym stopniu zależy od przykładowych danych.
źródło
Gotcha jest tym
simplejson
ijson
są to dwa różne moduły, przynajmniej w sposób, w jaki radzą sobie z Unicode. Maszjson
w py 2.6+, a to daje ci wartości Unicode, podczas gdysimplejson
zwraca obiekty łańcuchowe. Po prostu spróbuj easy_install-ing simplejson w swoim środowisku i sprawdź, czy to działa. To mi zrobiło.źródło
Po prostu użyj marynaty zamiast jsona do zrzutu i załadowania, tak:
Dane wyjściowe są generowane (ciągi i liczby całkowite są obsługiwane poprawnie):
źródło
safe_load
w YAML, nie wiem, czy istnieje coś podobnego do marynaty .Więc napotkałem ten sam problem. Zgadnij, jaki był pierwszy wynik Google.
Ponieważ muszę przekazać wszystkie dane do PyGTK, ciągi Unicode też nie są dla mnie bardzo przydatne. Mam więc inną metodę konwersji rekurencyjnej. W rzeczywistości jest również potrzebny do bezpiecznej konwersji JSON - json.dump () zwolniłby kaucję za wszelkie nie-literały, takie jak obiekty Python. Nie konwertuje jednak indeksów dict.
źródło
Miałem dykta JSON jako ciąg. Klucze i wartości były obiektami Unicode, jak w poniższym przykładzie:
Mógłbym użyć
byteify
funkcji sugerowanej powyżej, przekształcając ciąg znaków wdict
obiekt za pomocąast.literal_eval(myStringDict)
.źródło
{u'key':u'value'}
to nie JSON.Obsługa Python2 i 3 przy użyciu hooka (od https://stackoverflow.com/a/33571117/558397 )
Zwroty:
źródło
Jest późno na grę, ale zbudowałem ten rekurencyjny rzucający. Działa na moje potrzeby i myślę, że jest względnie kompletny. To może ci pomóc.
Po prostu przekaż mu obiekt JSON w taki sposób:
Mam go jako prywatnego członka klasy, ale możesz zmienić przeznaczenie metody według własnego uznania.
źródło
json.loads
najpierw potrzebne jest wywołanie), arbitralnie próbuje konwertować ciągi znaków na int bez żadnego wyjaśnionego powodu i nie kopiuje i- wklej gotowy.Przepisałem _parse_json () Wellsa, aby obsługiwać przypadki, w których sam obiekt json jest tablicą (mój przypadek użycia).
źródło
tutaj jest koder rekurencyjny napisany w C: https://github.com/axiros/nested_encode
Narzut wydajności dla „średnich” struktur około 10% w porównaniu do json.loads.
za pomocą tej struktury testowej:
źródło
W Pythonie 3.6 czasami napotykam ten problem. Na przykład podczas pobierania odpowiedzi z interfejsu API REST i ładowania tekstu odpowiedzi do JSON nadal otrzymuję ciągi Unicode. Znaleziono proste rozwiązanie za pomocą json.dumps ().
źródło
Wpadłem również na ten problem i mając do czynienia z JSON, wpadłem na małą pętlę, która konwertuje klucze Unicode na ciągi. (
simplejson
w GAE nie zwraca kluczy ciągów).obj
jest obiekt zdekodowany z JSON:kwargs
przekazuję konstruktorowi aplikacji GAE (która nie lubiunicode
kluczy**kwargs
)Nie tak solidne jak rozwiązanie Wellsa, ale znacznie mniejsze.
źródło
Mam dostosowany kod z odpowiedzią z Markiem Amery , w szczególności w celu pozbycia
isinstance
dla profesjonalistów z kaczki wzorcowego.Kodowanie odbywa się ręcznie i
ensure_ascii
jest wyłączone. Python docsjson.dump
mówi o tymUwaga: w doctest użyłem języka węgierskiego. Niektóre znaczące kodowania znaków związane z Węgrami to:
cp852
stosowane kodowanie IBM / OEM, np. w DOS (czasami nazywany ascii , błędnie myślę, że zależy to od ustawienia strony kodowej ),cp1250
używany np. w systemie Windows (czasem określanym jako ansi , w zależności od ustawień regionalnych), aiso-8859-2
czasem używany na serwerach HTTP. Tekst testowyTüskéshátú kígyóbűvölő
jest przypisany Koltai László (natywny formularz imienia) i pochodzi z wikipedii .Chciałbym również podkreślić jak odpowiedź z Jarret Hardie który References Spec JSON , cytując:
W moim przypadku użycia miałem pliki z Jsonem. Są to
utf-8
pliki zakodowane.ensure_ascii
powoduje powstanie poprawnie ukrytych, ale niezbyt czytelnych plików json, dlatego dostosowałem odpowiedź Marka Ameryka do moich potrzeb.Doctest nie jest szczególnie przemyślany, ale dzielę się kodem w nadziei, że komuś się przyda.
źródło
json.loads
będą listami lub dyktami, a nie jakimś typem zdefiniowanym przez użytkownika lub biblioteką, który implementuje swoje metody i metody magiczne, więc dlaczego po prostu nieisinstance
sprawdzić? Czy nie jest to łatwiejsze do zrozumienia niż sprawdzanie istnieniaiteritems
lubiter
akceptacji obiektu jako argumentu?Sprawdź tę odpowiedź na podobne pytanie, które to stwierdza
Prefiks u oznacza po prostu, że masz ciąg Unicode. Gdy naprawdę użyjesz ciągu, nie pojawi się on w twoich danych. Nie daj się wyrzucić wydrukowi.
Na przykład spróbuj tego:
Nie zobaczysz cię.
źródło
'{}'.format({u'x' : u'y'})
nadal obejmuje u.