Jak uzyskać obiekty string zamiast Unicode z JSON?

Używam Pythona 2 do parsowania JSON z zakodowanych w ASCII plików tekstowych.

Podczas wczytywania tych plików z json lub simplejson, wszystkie moje wartości łańcuchowe są wysyłane do obiektów Unicode zamiast do obiektów łańcuchowych. Problem w tym, że muszę używać danych z niektórymi bibliotekami, które akceptują tylko obiekty string. Nie mogę zmienić bibliotek ani ich zaktualizować.

Czy możliwe jest uzyskanie obiektów string zamiast Unicode jeden?

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`

Update

To pytanie zostało zadane dawno temu, kiedy utknąłem z Pythonem 2. Jednym z łatwych i czystych rozwiązań na dziś jest użycie najnowszej wersji Pythona - tj. Python 3 i forward.

Author: Brutus, 2009-06-05

21 answers

Rozwiązanie z object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

Przykładowe użycie:

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

Jak to działa i dlaczego miałbym go używać?

Funkcja Marka Amery ' ego jest krótsza i jaśniejsza od tych, więc jaki jest ich sens? Dlaczego chcesz ich użyć?

Wyłącznie dla wykonania. Mark ' s answer dekoduje tekst JSON w pełni najpierw za pomocą ciągów unicode, a następnie rekursuje całą dekodowaną wartość, aby przekonwertować wszystkie ciągi na ciągi bajtów. To ma kilka działań niepożądanych:

  • Kopia całej zdekodowanej struktury zostaje utworzona w pamięci
  • Jeśli twój obiekt JSON jest naprawdę głęboko zagnieżdżony (500 poziomów lub więcej), to osiągniesz maksymalną głębokość rekursji Pythona

Ta odpowiedź łagodzi oba te problemy z wydajnością, używając parametru object_hook z json.load i json.loads. Z docs :

object_hook jest funkcją opcjonalną, która zostanie wywołana z wynikiem dowolnego obiekt dosłowny dekodowany (a dict). Zwracana wartość object_hook zostanie użyta zamiast dict. Ta funkcja może być używana do implementacji niestandardowych dekoderów

Ponieważ słowniki zagnieżdżone wiele poziomów głęboko w innych słownikach przechodzą do object_hook ponieważ są one dekodowane , możemy bajteifikować dowolne łańcuchy lub listy wewnątrz nich w tym momencie i uniknąć potrzeby późniejszej głębokiej rekurencji.

Odpowiedź marka nie nadaje się do użycia jako object_hook w obecnej postaci, ponieważ rekursuje do zagnieżdżonych słowników. Zapobiegamy tej rekurencji w tej odpowiedzi za pomocą parametru ignore_dicts na _byteify, który jest przekazywany do niego przez cały czas Z wyjątkiem, gdy object_hook przekazuje mu nowe dict do byteify. Znacznik ignore_dicts mówi _byteify, aby ignorować dict s, ponieważ zostały one już bajtowane.

Wreszcie, nasze implementacje json_load_byteified i json_loads_byteified wywołują _byteify (z ignore_dicts=True) NA wyniku zwróconym z json.load lub json.loads, aby obsłużyć przypadek, w którym dekodowany tekst JSON nie ma dict w najwyższy poziom.

 80
Author: Mirec Miskuf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:14

Chociaż jest kilka dobrych odpowiedzi tutaj, skończyło się na użyciuPyYAML do analizy moich plików JSON, ponieważ daje klucze i wartości jako str type strings zamiast unicode type. Ponieważ JSON jest podzbiorem YAML to działa ładnie:

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

Uwagi

Kilka rzeczy do zauważenia:

  • Otrzymuję obiekty string ponieważ wszystkie moje wpisy są zakodowane ASCII . Jeśli użyłbym wpisów zakodowanych w unicode, otrzymałbym je z powrotem jako unicode obiekty - Nie ma konwersji!

  • Powinieneś (prawdopodobnie zawsze) używać funkcji safe_load Pyyamla; jeśli używasz jej do ładowania plików JSON, nie potrzebujesz "dodatkowej mocy" funkcji load.

  • Jeśli chcesz parser YAML, który ma więcej wsparcia dla wersji 1.2 spec (i poprawnie parsuje bardzo niskie liczby ) spróbuj Ruamel YAML: pip install ruamel.yaml and import ruamel.yaml as yaml was all I needed in my testy.

Konwersja

Jak stwierdzono, nie ma nawrócenia! Jeśli nie możesz być pewien, że zajmujesz się tylko wartościami ASCII (i nie możesz być pewien przez większość czasu), lepiej Użyj funkcji konwersji:

Używałem tej z Mark Amery teraz kilka razy, działa świetnie i jest bardzo łatwy w użyciu. Możesz również użyć podobnej funkcji jako object_hook, ponieważ może to zwiększyć wydajność dużych plików. Zobacz też: odpowiedź od Mirec Miskuf za to.

 164
Author: Brutus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:53

Nie ma wbudowanej opcji, aby Funkcje modułu json zwracały ciągi bajtów zamiast ciągów unicode. Jednak ta krótka i prosta funkcja rekurencyjna przekonwertuje dowolny zdekodowany obiekt JSON z ciągów unicode na kodowane UTF-8 ciągi bajtów:

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

Po prostu wywołaj to na wyjściu otrzymanym z json.load lub json.loads.

Kilka uwag:

  • Aby Obsługiwać Python 2.6 lub wcześniejszy, zamień return {byteify(key): byteify(value) for key, value in input.iteritems()} na return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), ponieważ składanie słownika nie było obsługiwane do Pythona 2.7.
  • ponieważ ta odpowiedź przenika przez cały zdekodowany obiekt, ma ona kilka niepożądanych cech wydajności, których można uniknąć przy bardzo ostrożnym użyciu parametrów object_hook lub object_pairs_hook. odpowiedź Mireca Miskufa jest jak na razie jedyną, która potrafi to zrobić poprawnie, chociaż w konsekwencji jest to znacznie bardziej skomplikowane niż moje podejście.
 137
Author: Mark Amery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:26

Możesz użyć parametru object_hook dla json.loads aby przejść w konwerterze. Nie musisz robić nawrócenia po fakcie. Na json Moduł zawsze przekaże tylko dicty object_hook i będzie przechodził rekurencyjnie w zagnieżdżonych dictach, więc nie musisz sam rekurencyjnie przechodzić do zagnieżdżonych dictów. Nie sądzę, bym konwertował ciągi unicode na liczby jak Wells shows. 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).

Również starałbym się unikać robienia czegoś takiego jak str(val) na unicode obiekcie. Powinieneś używać value.encode(encoding) z poprawnym kodowaniem, w zależności od tego, czego oczekuje twój zewnętrzny lib.

Więc, na przykład:

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)
 73
Author: Mike Brennan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 17:33:12

Dzieje się tak dlatego, że json nie ma różnicy między obiektami string i Unicode. Wszystkie są ciągami w javascript.

Myślę, że JSON ma rację zwracając obiekty unicode . W rzeczywistości nie akceptowałbym niczego mniej, ponieważ łańcuchy javascript są w rzeczywistości unicode obiektami (np. łańcuchy JSON (javascript) mogą przechowywać dowolny rodzaj znaku unicode), więc sensowne jest tworzenie unicode obiektów przy tłumaczeniu łańcuchów z JSON. Zwykłe struny po prostu nie pasują od biblioteka musiałaby odgadnąć kodowanie, które chcesz.

Lepiej używać unicode obiektów string wszędzie. Więc najlepszą opcją jest aktualizacja bibliotek, aby mogły radzić sobie z obiektami unicode.

Ale jeśli naprawdę chcesz bajtstrings, po prostu Zakoduj wyniki do wybranego kodowania:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']
 37
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-05 18:29:06

Istnieje łatwe obejście.

TL; DR-Użyj ast.literal_eval() zamiast json.loads(). Zarówno ast, jak i json znajdują się w bibliotece standardowej.

Chociaż nie jest to odpowiedź "idealna", to jest dość daleko, jeśli planujesz całkowicie zignorować Unicode. W Pythonie 2.7

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

Daje:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

Robi się to bardziej owłosione, gdy niektóre obiekty są ciągami Unicode. Pełna odpowiedź robi się Owłosiona szybko.

 14
Author: Charles Merriam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-07 01:01:43

Obawiam się, że nie ma możliwości osiągnięcia tego automatycznie w bibliotece simplejson.

Skaner i dekoder w simplejson są zaprojektowane do tworzenia tekstu unicode. Aby to zrobić, biblioteka używa funkcji o nazwie c_scanstring (jeśli jest dostępna, dla szybkości) lub py_scanstring, Jeśli wersja C nie jest dostępna. Funkcja scanstring jest wywoływana kilka razy przez prawie każdą procedurę, którą simplejson ma do dekodowania struktury, która może zawierać tekst. Musisz albo monkeypatch wartość scanstring w simplejson.dekoder lub podklasa JSONDecoder i zapewnij prawie całą własną implementację wszystkiego, co może zawierać tekst.

Powodem, dla którego simplejson wysyła unicode, jest to, że JSON spec wyraźnie wspomina, że "łańcuch jest zbiorem zero lub więcej znaków Unicode"... obsługa unicode jest przyjmowana jako część samego formatu. Implementacja scanstring simplejsona sięga tak daleko, że skanuje i interpretuje ucieczki unicode (nawet sprawdzanie błędów dla zniekształcone wielobajtowe reprezentacje znaków), więc jedynym sposobem na niezawodne zwrócenie ci wartości jest unicode.

Jeśli masz starszą bibliotekę, która wymaga str, polecam albo mozolne przeszukiwanie zagnieżdżonej struktury danych po przetworzeniu (co przyznaję jest tym, czego wyraźnie powiedziałeś, że chcesz uniknąć... sorry), a może owinąć biblioteki w jakąś fasadę, gdzie można masować parametry wejściowe na bardziej ziarnistym poziomie. Drugie podejście może być bardziej zarządzalne niż pierwsze, jeśli struktury danych są rzeczywiście głęboko zagnieżdżone.

 9
Author: Jarret Hardie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-05 18:10:03

Odpowiedź Mike ' a Brennana jest bliska, ale nie ma powodu, by przechodzić przez całą strukturę. Jeśli używasz object_hook_pairs (Python 2.7+) parametr:

object_pairs_hook jest opcjonalną funkcją, która zostanie wywołana z wynikiem dowolnego obiektu literalnie zdekodowanego z uporządkowaną listą par. Wartość zwracana object_pairs_hook zostanie użyty zamiast dict. Ta funkcja może być wykorzystana do wdrożenia niestandardowych dekoderów, które opierają się na kolejności, że klucz i pary wartości są dekodowane(na przykład collections.OrderedDict zapamięta kolejność wstawiania). Jeśli object_hook określa się również, że object_pairs_hook ma pierwszeństwo.

Dzięki niemu otrzymujesz każdy obiekt JSON, więc możesz dekodować bez potrzeby rekurencji:]}
def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'                                        

In [53]: json.load(open('test.json'))
Out[53]: 
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

Zauważ, że nigdy nie muszę wywoływać Hooka rekurencyjnie, ponieważ każdy obiekt zostanie przekazany hookowi, gdy użyjesz object_pairs_hook. Trzeba troszczyć się o listy, ale jak widać, obiekt wewnątrz lista zostanie poprawnie przekonwertowana i nie musisz rekurencyjnie jej wykonywać.

EDIT: współpracownik zauważył, że Python2.6 nie ma object_hook_pairs. Nadal możesz użyć tego will Python2. 6, dokonując bardzo małej zmiany. W haku powyżej, zmiana:

for key, value in pairs:

Do

for key, value in pairs.iteritems():

Następnie użyj object_hook zamiast object_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

Użycie object_pairs_hook powoduje utworzenie jednego słownika mniej dla każdego obiektu w obiekcie JSON, co, jeśli analizujesz ogromny dokument, może warto.

 9
Author: Travis Jensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:33

Jak poprawnie zauważa Mark (Amery): użycie deserializera PyYaml na zrzut json działa tylko wtedy, gdy masz tylko ASCII. Przynajmniej po wyjęciu z pudełka.

Dwa szybkie komentarze na temat podejścia PyYaml:

  1. Nigdy nie używaj yaml.załaduj dane z pola. Jego funkcja (!) yaml do wykonania dowolnego kodu ukrytego w strukturze.

  2. You can make it work also for non ASCII via this:

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)
    

Ale wydajność nie ma porównania do odpowiedzi Marka Amery ' ego:

Rzucając kilka głęboko zagnieżdżonych dictów próbek na dwie metody, rozumiem to (z DT [j] = delta czasu JSON.Łady(json."dumps (m)"): {]}

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

Więc deserializacja obejmująca pełne poruszanie się po drzewie i kodowanie , dokładnie w granicach rzędu wielkości implementacji opartej na json C. Uważam, że jest to niezwykle szybkie, a także bardziej wytrzymałe niż obciążenie yaml przy głęboko zagnieżdżonych strukturach. I mniej podatne na błędy bezpieczeństwa, patrząc na yaml.ładuj.

=> chociaż byłbym wdzięczny za wskaźnik do konwertera opartego tylko na C, to funkcja byteify powinna być domyślną odpowiedzią.

Jest to szczególnie ważne, jeśli twoja struktura json pochodzi z pola zawierającego dane wejściowe użytkownika. Ponieważ wtedy prawdopodobnie będziesz musiał przejść po swojej strukturze-niezależnie od pożądanych wewnętrznych struktur danych(tylko 'Unicode sandwich' lub ciągi bajtów).

Dlaczego?

Unicode normalizacja . Na weź środek przeciwbólowy i przeczytaj to .

Więc używając rekurencji byteify zabijasz dwie pieczenie jednym kamieniem:

  1. pobieraj bajty z zagnieżdżonych zrzutów json
  2. znormalizuj wartości wejściowe użytkownika, aby znaleźć rzeczy w magazynie.

W moich testach okazało się, że wymiana wejścia.kodowanie ('utf-8') za pomocą unicodedata.normalize ('NFC', input).kodowanie ('utf-8') było jeszcze szybsze niż w/O NFC - ale to w dużym stopniu zależy od próbki chyba data.

 4
Author: Red Pill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-14 17:42:03

The gotcha is that simplejson and json are two different modules, at least in the way they deal with unicode. MASZ json w py 2.6+, co daje Ci wartości unicode, podczas gdy simplejson zwraca obiekty string. Po prostu spróbuj easy_install-ING simplejson w swoim środowisku i sprawdź, czy to działa. Dla mnie tak.

 3
Author: ducu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-19 19:48:34

Więc napotkałem ten sam problem. Zgadnij, jaki był pierwszy wynik Google.

Ponieważ muszę przekazać wszystkie dane do PyGTK, łańcuchy unicode też nie są dla mnie zbyt przydatne. Mam więc inną rekurencyjną metodę konwersji. Jest również potrzebny do konwersji typu JSON - json.dump() zwalnia wszystkie Nie-literały, takie jak obiekty Pythona. Nie konwertuje jednak indeksów dict.

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj
 1
Author: mario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-05 18:22:51

Po prostu użyj pickle zamiast json do zrzutu i załadowania, w ten sposób:

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

Generowane przez niego wyjście to (ciągi i liczby całkowite są obsługiwane poprawnie):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}
 1
Author: Stefan Gruenwald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-27 20:15:01

Obsługa Python2 i 3 za pomocą Hooka (z https://stackoverflow.com/a/33571117/558397 )

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) for item in data ]

    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

Zwraca:

 {'three': '', 'key': 'value', 'one': 'two'}
 1
Author: abarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 20:16:47

Jest późno na grę, ale zbudowałem ten rekurencyjny caster. Działa na moje potrzeby i myślę, że jest stosunkowo kompletny. To może Ci pomóc.

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

Po prostu podaj obiekt JSON w ten sposób:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)
Mam to jako prywatny członek klasy, ale możesz zmienić sposób, jak uważasz za stosowne.
 0
Author: Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-29 03:53:43

Przepisałem _parse_json () Wellsa do obsługi przypadków, w których sam obiekt json jest tablicą (mój przypadek użycia).

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj
 0
Author: darnmarshall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-07 05:12:22

Oto koder rekurencyjny napisany w C: https://github.com/axiros/nested_encode

Wydajność dla "średnich" struktur około 10% w porównaniu do json.mnóstwo.

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

Za pomocą tego testu:

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)
 0
Author: Red Pill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 12:44:46

Miałem JSON dict jako ciąg. Klucze i wartości były obiektami unicode, jak w poniższym przykładzie:

myStringDict = "{u'key':u'value'}"

Mógłbym użyć funkcji byteify zaproponowanej powyżej, konwertując łańcuch znaków na obiekt dict za pomocą ast.literal_eval(myStringDict).

 0
Author: narko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-17 15:17:21

Sprawdź to odpowiedź na podobne pytanie, które stwierdza, że

Przedrostek u oznacza tylko, że masz ciąg Unicode. Gdy naprawdę użyjesz tego ciągu, nie pojawi się on w Twoich danych. Nie daj się wyrzucić przez wydrukowane wyjście.

Na przykład, spróbuj tego:

print mail_accounts[0]["i"]

Nie zobaczysz u.

 0
Author: kunal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-04 13:32:45

W Pythonie 3.6, czasami wciąż napotykam ten problem. Na przykład, gdy otrzymuję odpowiedź z REST API i ładuję tekst odpowiedzi do JSON, nadal otrzymuję ciągi znaków unicode. Znalazłem proste rozwiązanie przy użyciu json.zrzuty ().

response_message = json.loads(json.dumps(response.text))
print(response_message)
 0
Author: Yuelin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 17:17:55

Też napotkałem ten problem i mając do czynienia z JSONEM, wpadłem na małą pętlę, która konwertuje klucze unicode na ciągi. (simplejson na GAE nie zwraca kluczy łańcuchowych.)

obj jest obiektem zdekodowanym z JSON:

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargs jest to, co przekazuję konstruktorowi aplikacji GAE (która nie lubi unicode kluczy w **kwargs)

Nie tak solidne jak rozwiązanie ze studni, ale znacznie mniejsze.
 -1
Author: boatcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 17:01:18

Zaadaptowałem kod z odpowiedzi z Marka Amery'ego , szczególnie po to, aby pozbyć się isinstance dla profesjonalistów z Duck-typingu.

Kodowanie jest wykonywane ręcznie i ensure_ascii jest wyłączone. Python docs for json.dump mówi, że

Jeśli ensure_ascii jest prawdziwe( domyślne), wszystkie znaki spoza ASCII na wyjściu są przetwarzane za pomocą sekwencji \uXXXX

Zastrzeżenie: w doctest użyłem języka węgierskiego. Niektóre godne uwagi Kodowanie znaków związane z językiem węgierskim to: cp852 kodowanie IBM/OEM używane np. w DOS (czasami określane jako ascii , błędnie myślę, że zależy to od ustawienia codepage ), cp1250 używane np. w Windows (czasami określane jako ansi, zależne od ustawień regionalnych) i iso-8859-2, czasami używane na serwerach http. Tekst testowy Tüskéshátú kígyóbűvölő przypisany jest Koltai László (rodzima forma imienia) i pochodzi z Wikipedii.

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

I ' d chciałbym również podkreślić odpowiedź z Jarret Hardie , która odwołuje się do JSON spec , cytując:

Łańcuch jest zbiorem zero lub więcej znaków Unicode

W moim przypadku użycia miałem pliki z json. Są to utf-8 zakodowane pliki. ensure_ascii powoduje poprawne ucieczki, ale niezbyt czytelne pliki json, dlatego dostosowałem odpowiedź Marka Amery ' ego do moich potrzeb.

Doctest nie jest specjalnie przemyślany, ale podzielam kod w nadziei, że będzie przydatny dla kogoś.

 -1
Author: n611x007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-03 07:59:46