2016-03-15 23 views
12

Mam plik json, który ma wiele chińskich i japońskich (i innych języków) znaków. Ja ładuje go do mojego skryptu Pythona 2.7 za pomocą io.open następująco:Json.dump kończy się niepowodzeniem z "musi być unicode, a nie str" TypeError

with io.open('multiIdName.json', encoding="utf-8") as json_data: 
    cards = json.load(json_data) 

dodać nowy obiekt do JSON, wszystko dobrze. Wtedy staram się go napisać z powrotem do innego pliku:

with io.open("testJson.json",'w',encoding="utf-8") as outfile: 
     json.dump(cards, outfile, ensure_ascii=False) 

To wtedy pojawia się błąd TypeError: must be unicode, not str

próbowałem pisać outfile jako binarna (with io.open("testJson.json",'wb') as outfile:), ale w końcu z rzeczy to:

{"multiverseid": 262906, "name": "\u00e6\u00b8\u00b8\u00e9\u009a\u00bc\u00e7\u008b\u00ae\u00e9\u00b9\u00ab", "language": "Chinese Simplified"} 

Myślałem, że otwarcie i pisanie w tym samym kodowaniu będzie wystarczające, jak również flaga sure_ascii, ale oczywiście nie. Chcę tylko zachować znaki, które istniały w pliku przed uruchomieniem mojego skryptu, bez ich przekształcania w \ u.

+0

nie jestem pewien. Wierzę, że dzieje się tak dlatego, że otwierasz wskaźnik pliku jako zakodowany plik utf-8, ale wyrzucasz obiekt typu "string" ('cards'). –

+0

Ach, powinienem wspomnieć, że karty to obiekt json: '' 'cards = json.load (json_data)' '' – IronWaffleMan

+0

jaka jest nowa właściwość, którą dodasz? czy możliwe jest napisanie [Minimalny sprawdzalny przykład] (http://stackoverflow.com/help/mcve)? –

Odpowiedz

19

Czy możesz wypróbować następujące rzeczy?

with io.open("testJson.json",'w',encoding="utf-8") as outfile: 
    outfile.write(unicode(json.dumps(cards, ensure_ascii=False))) 
+1

Wydaje się, że udało się, dzięki. Zakładam, że outfile.write pobiera dane wyjściowe z json.dumps, a następnie zapisuje je do pliku? – IronWaffleMan

+1

Świetne :) Tak. outfile.write (content) - zapisuje zawartość do pliku wyjściowego. Natomiast plik wyjściowy odnosi się do pliku "testJson.json". zobacz więcej w https://docs.python.org/2/tutorial/inputoutput.html – Yaron

+0

Niebezpieczeństwo! Masz domyślną konwersję str-> Unicode, bez zdefiniowanego kodowania. W Pythonie 2.x domyślnym kodowaniem jest ASCII, więc otrzymasz wyjątek 'UnicodeDecodeError', jeśli twój JSON zawiera znaki spoza ASCII –

3

Przyczyną tego błędu jest całkowicie głupie zachowanie json.dumps w Pythonie 2:

>>> json.dumps({'a': 'a'}, ensure_ascii=False) 
'{"a": "a"}' 
>>> json.dumps({'a': u'a'}, ensure_ascii=False) 
u'{"a": "a"}' 
>>> json.dumps({'a': 'ä'}, ensure_ascii=False) 
'{"a": "\xc3\xa4"}' 
>>> json.dumps({u'a': 'ä'}, ensure_ascii=False) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.7/json/__init__.py", line 250, in dumps 
    sort_keys=sort_keys, **kw).encode(obj) 
    File "/usr/lib/python2.7/json/encoder.py", line 210, in encode 
    return ''.join(chunks) 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128) 

To w połączeniu z faktem, że io.open z encoding ustawiony tylko akceptuje unicode obiektów (który sam w sobie jest w porządku), prowadzi do problemów.

Typ zwracany jest całkowicie zależny od typu kluczy lub wartości w słowniku, jeśli ensure_ascii=False, ale str jest zwracana zawsze, jeśli ensure_ascii=True. Jeśli można przypadkowo ustawić 8-bitowe ciągi do słowników, nie można ślepo przekonwertować ten typ zwracany do unicode, bo potrzeba ustawić kodowanie, przypuszczalnie UTF-8

>>> x = json.dumps(obj, ensure_ascii=False) 
>>> if isinstance(x, str): 
...  x = unicode(x, 'UTF-8') 

W ten przypadku I wierzę, że możesz użyć json.dump, aby pisać do otwartego pliku binarnego; jednak, jeśli potrzebujesz czegoś bardziej skomplikowanego z otrzymanym obiektem, prawdopodobnie potrzebujesz powyższego kodu.


Jednym z rozwiązań jest, aby zakończyć wszystkie kodowania/dekodowania obłęd poprzez przełączenie do Pythona 3.

+0

Myślę, że twoja odpowiedź lepiej odpowiada na pytanie. Chcesz dodać część do swojej odpowiedzi, a ja usunę odpowiedź? –

+0

@AlastairMcCormack Naah zajęty teraz, a ja trafiłem już dzisiaj w repcap: D –

+0

Jeśli udało mi się przekonwertować skrypt na py3, jak zmieniłaby się obsługa kodowania? – IronWaffleMan

3

Moduł JSON obsługuje kodowanie i dekodowanie dla ciebie, więc można po prostu otworzyć pliki wejściowe i wyjściowe w formacie binarnym tryb. Moduł JSON przyjmuje kodowanie UTF-8, ale można go zmienić za pomocą atrybutu encoding metodami load() i dump().

with open('multiIdName.json', 'rb') as json_data: 
    cards = json.load(json_data) 

następnie:

with open("testJson.json", 'wb') as outfile: 
    json.dump(cards, outfile, ensure_ascii=False) 

Dzięki @Antti Haapala, Python 2.x moduł JSON daje albo Unicode lub str zależności od zawartości obiektu.

Trzeba będzie włożyć czek sens, aby zapewnić wynik jest Unicode przed piśmie za pośrednictwem io:

with io.open("testJson.json", 'w', encoding="utf-8") as outfile: 
    my_json_str = json.dumps(my_obj, ensure_ascii=False) 
    if isinstance(my_json_str, str): 
     my_json_str = my_json_str.decode("utf-8") 

    outfile.write(my_json_str) 
+0

Kiedy to zrobię, otrzymuję: '' 'UnicodeEncodeError: kodek 'ascii' nie może kodować znaków w pozycji 1-2: porządkowa nie w zakresie (128)' '' – IronWaffleMan

+0

Z której linii? –

+0

Czy na pewno ustawiłeś tryb 'b' dla obu wywołań' open() '? –