Как получить строковые объекты вместо Unicode из JSON? - PullRequest
264 голосов
/ 05 июня 2009

Я использую Python 2 для анализа JSON из ASCII-кодированных текстовых файлов.

При загрузке этих файлов с json или simplejson все мои строковые значения преобразуются в объекты Unicode вместо строковых объектов. Проблема в том, что я должен использовать данные с некоторыми библиотеками, которые принимают только строковые объекты. Я не могу ни изменить библиотеки , ни обновить их.

Возможно ли получить строковые объекты вместо Unicode?

Пример

>>> 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`

Обновление

Этот вопрос был задан давным-давно , когда я застрял с Python 2 . Одним из простых и понятных решений на сегодняшний день является использование последней версии Python - т.е. Python 3 и более поздних версий.

Ответы [ 21 ]

2 голосов
/ 28 апреля 2014

Просто используйте pickle вместо json для dump и load, вот так:

    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"))

Выходные данные (строки и целые числа обрабатываются правильно):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}
1 голос
/ 21 августа 2017

Поддержка Python2 & 3 с помощью хука (от 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)

Возвращает:

 {'three': '', 'key': 'value', 'one': 'two'}
1 голос
/ 05 июля 2010

Итак, я столкнулся с той же проблемой. Угадайте, какой был первый результат Google.

Поскольку мне нужно передать все данные в PyGTK, строки Unicode для меня тоже не очень полезны. Так что у меня есть другой метод рекурсивного преобразования. Это на самом деле также необходимо для безопасных типов JSON-преобразований - json.dump () будет вызывать любые не-литералы, например объекты Python. Однако не конвертирует индексы 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
0 голосов
/ 25 апреля 2018

В Python 3.6 иногда я все еще сталкиваюсь с этой проблемой. Например, при получении ответа от REST API и загрузке текста ответа в JSON, я все еще получаю строки Unicode. Нашел простое решение с помощью json.dumps ().

response_message = json.loads(json.dumps(response.text))
print(response_message)
0 голосов
/ 04 июля 2017

Проверьте этот ответ на подобный вопрос, который утверждает, что

Префикс u означает, что у вас есть строка Unicode. Когда вы действительно используете строку, она не появится в ваших данных. Не поддавайтесь распечатке.

Например, попробуйте это:

print mail_accounts[0]["i"]

Ты не увидишь.

0 голосов
/ 29 октября 2009

Это поздно в игре, но я построил этот рекурсивный заклинатель. Это работает для моих нужд, и я думаю, что это относительно полно. Это может помочь вам.

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

Просто передайте ему объект JSON следующим образом:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

У меня есть личный член класса, но вы можете использовать метод по своему усмотрению.

0 голосов
/ 23 июня 2015

Я адаптировал код из ответа из Марка Эмери , особенно для того, чтобы избавиться от isinstance для плюсов утки.

Кодирование выполняется вручную и ensure_ascii отключено. Документы Python для json.dump говорят, что

Если для параметра гарантировано задано значение True (по умолчанию), все символы, не входящие в ASCII, в выходных данных экранируются последовательностями \ uXXXX

Отказ от ответственности: в doctest я использовал венгерский язык. Некоторые известные венгерские кодировки символов: cp852 используемая кодировка IBM / OEM, например. в DOS (иногда его называют ascii , я думаю, что это неправильно, это зависит от настройки codepage ), cp1250 используется, например. в Windows (иногда ее называют ansi , в зависимости от настроек локали) и iso-8859-2, иногда используемой на http-серверах. Текст теста Tüskéshátú kígyóbűvölő относится к Koltai László (родная личная форма имени) и из wikipedia .

# 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)

Я также хотел бы выделить ответ из Джаррет Харди , который ссылается на JSON spec , цитата:

Строка - это набор из нуля или более символов Юникода

В моем сценарии использования у меня были файлы с json. Это utf-8 закодированные файлы. ensure_ascii приводит к правильно экранированным, но не очень читаемым файлам json, поэтому я адаптировал ответ Марка Эмери для своих нужд.

doctest не особенно продуман, но я делюсь кодом в надежде, что он кому-нибудь пригодится.

0 голосов
/ 04 мая 2015

вот рекурсивный кодировщик, написанный на C: https://github.com/axiros/nested_encode

Снижение производительности для «средних» структур около 10% по сравнению с json.loads.

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%

с использованием этой тестовой структуры:

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 голосов
/ 30 января 2015

У меня был JSON dict в виде строки. Ключи и значения были объектами Unicode, как в следующем примере:

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

Я мог бы использовать предложенную выше функцию byteify, преобразовав строку в объект dict, используя ast.literal_eval(myStringDict).

0 голосов
/ 07 июня 2013

Я переписал _parse_json () Уэллса для обработки случаев, когда сам объект json является массивом (мой вариант использования).

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
...