Обработка ленивого JSON в Python – «Ожидание названия свойства»

Используя модуль Pythons (2.7) 'json', я хочу обработать различные каналы JSON. К сожалению, некоторые из этих каналов не соответствуют стандартам JSON – в некоторых некоторых ключах не завертываются двойные речевые метки («). Это приводит к ошибке Python.

Прежде чем писать фрагмент кода с уродливым адским кодом, чтобы разобрать и восстановить входящие данные, я подумал, что спрошу – есть ли способ разрешить Python либо проанализировать этот искаженный JSON, либо «восстановить» данные, чтобы он был действительный JSON?

Рабочий пример

import json >>> json.loads('{"key1":1,"key2":2,"key3":3}') {'key3': 3, 'key2': 2, 'key1': 1} 

Неисправный пример

 import json >>> json.loads('{key1:1,key2:2,key3:3}') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Python27\lib\json\__init__.py", line 310, in loads return _default_decoder.decode(s) File "C:\Python27\lib\json\decoder.py", line 346, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "C:\Python27\lib\json\decoder.py", line 362, in raw_decode obj, end = self.scan_once(s, idx) ValueError: Expecting property name: line 1 column 1 (char 1) 

Я написал небольшой REGEX, чтобы исправить JSON, исходящий от этого конкретного провайдера, но я предвижу, что это будет проблемой в будущем. Ниже я придумал.

 >>> import re >>> s = '{key1:1,key2:2,key3:3}' >>> s = re.sub('([{,])([^{:\s"]*):', lambda m: '%s"%s":'%(m.group(1),m.group(2)),s) >>> s '{"key1":1,"key2":2,"key3":3}' 

  • Какой модуль JSON можно использовать в Python 2.5?
  • Разбор вложенных JSON и запись их в CSV
  • <Объект Django> не является сериализуемым JSON
  • Сглаживание общего JSON Список диктов или списков в Python
  • Python скрежет JSON конвертирует в CSV
  • Сообщение JSON с использованием запросов Python
  • json.loads не работает с двоичным JSON, отправленным AngularJS в Python
  • Попытка вытащить сообщения и комментарии из Facebook для проекта, но не может получить все (python, json)
  • 6 Solutions collect form web for “Обработка ленивого JSON в Python – «Ожидание названия свойства»”

    Вы пытаетесь использовать парсер JSON для анализа того, что не является JSON. Лучше всего заставить создателя фидов исправить их.

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

     j = re.sub(r"{\s*(\w)", r'{"\1', j) j = re.sub(r",\s*(\w)", r',"\1', j) j = re.sub(r"(\w):", r'\1":', j) 

    Другой вариант – использовать модуль demjson, который может анализировать json в нестрогом режиме.

    Регулярные выражения, отмеченные Ned и cheeseinvert, не учитываются, когда совпадение находится внутри строки.

    См. Следующий пример (с использованием решения soninvert):

     >>> fixLazyJsonWithRegex ('{ key : "a { a : b }", }') '{ "key" : "a { "a": b }" }' 

    Проблема в том, что ожидаемый результат:

     '{ "key" : "a { a : b }" }' 

    Поскольку токены JSON являются подмножеством токенов python, мы можем использовать модуль tokenize python.

    Пожалуйста, исправьте меня, если я ошибаюсь, но следующий код исправит ленивую строку json во всех случаях:

     import tokenize import token from StringIO import StringIO def fixLazyJson (in_text): tokengen = tokenize.generate_tokens(StringIO(in_text).readline) result = [] for tokid, tokval, _, _, _ in tokengen: # fix unquoted strings if (tokid == token.NAME): if tokval not in ['true', 'false', 'null', '-Infinity', 'Infinity', 'NaN']: tokid = token.STRING tokval = u'"%s"' % tokval # fix single-quoted strings elif (tokid == token.STRING): if tokval.startswith ("'"): tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"') # remove invalid commas elif (tokid == token.OP) and ((tokval == '}') or (tokval == ']')): if (len(result) > 0) and (result[-1][1] == ','): result.pop() # fix single-quoted strings elif (tokid == token.STRING): if tokval.startswith ("'"): tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"') result.append((tokid, tokval)) return tokenize.untokenize(result) 

    Таким образом, чтобы разобрать json-строку, вы можете захотеть инкапсулировать вызов fixLazyJson, когда json.loads завершится с ошибкой (чтобы избежать штрафных санкций за хорошо сформированный json):

     import json def json_decode (json_string, *args, **kwargs): try: json.loads (json_string, *args, **kwargs) except: json_string = fixLazyJson (json_string) json.loads (json_string, *args, **kwargs) 

    Единственная проблема, которую я вижу при установке lazy json, заключается в том, что если json неверен, ошибка, вызванная вторым json.loads, не будет ссылаться на строку и столбец из исходной строки, а на измененную.

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

    БОНУС: Кроме того, людям обычно нравится включать комментарии C / C ++, когда json используется для файлов конфигурации, в этом случае вы можете либо удалять комментарии с использованием обычного выражения , либо использовать расширенную версию, и исправить строку json за один проход :

     import tokenize import token from StringIO import StringIO def fixLazyJsonWithComments (in_text): """ Same as fixLazyJson but removing comments as well """ result = [] tokengen = tokenize.generate_tokens(StringIO(in_text).readline) sline_comment = False mline_comment = False last_token = '' for tokid, tokval, _, _, _ in tokengen: # ignore single line and multi line comments if sline_comment: if (tokid == token.NEWLINE) or (tokid == tokenize.NL): sline_comment = False continue # ignore multi line comments if mline_comment: if (last_token == '*') and (tokval == '/'): mline_comment = False last_token = tokval continue # fix unquoted strings if (tokid == token.NAME): if tokval not in ['true', 'false', 'null', '-Infinity', 'Infinity', 'NaN']: tokid = token.STRING tokval = u'"%s"' % tokval # fix single-quoted strings elif (tokid == token.STRING): if tokval.startswith ("'"): tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"') # remove invalid commas elif (tokid == token.OP) and ((tokval == '}') or (tokval == ']')): if (len(result) > 0) and (result[-1][1] == ','): result.pop() # detect single-line comments elif tokval == "//": sline_comment = True continue # detect multiline comments elif (last_token == '/') and (tokval == '*'): result.pop() # remove previous token mline_comment = True continue result.append((tokid, tokval)) last_token = tokval return tokenize.untokenize(result) 

    Расширившись по предложению Неда, мне было полезно следующее:

     j = re.sub(r"{\s*'?(\w)", r'{"\1', j) j = re.sub(r",\s*'?(\w)", r',"\1', j) j = re.sub(r"(\w)'?\s*:", r'\1":', j) j = re.sub(r":\s*'(\w+)'\s*([,}])", r':"\1"\2', j) 

    В аналогичном случае я использовал ast.literal_eval . AFAIK, это не сработает, только когда в JSON появится константа null (соответствующая Python None ).

    Учитывая, что вы знаете о null/None , вы можете:

     import ast decoded_object= ast.literal_eval(json_encoded_text) 

    В дополнение к предложениям Neds и cheeseinvert добавление (?!/) Должно избегать указанной проблемы с URL-адресами

     j = re.sub(r"{\s*'?(\w)", r'{"\1', j) j = re.sub(r",\s*'?(\w)", r',"\1', j) j = re.sub(r"(\w)'?\s*:(?!/)", r'\1":', j) j = re.sub(r":\s*'(\w+)'\s*([,}])", r':"\1"\2', j) j = re.sub(r",\s*]", "]", j) 
    Interesting Posts

    Ошибка компиляции пин-инструмента с помощью python включает в себя получение ошибки C2872: 'UINT32': неоднозначный символ

    Как преобразовать XSD в класс Python

    Python: секунды с эпохи до относительной даты

    Django inlineformset_factory и ManyToMany

    как я могу найти и удалить наложенные фрагменты изображения из списка?

    Как установить пакет python в определенном каталоге

    Случайное Python: Что произойдет, если я не использую семя (someValue)?

    Как преобразовать строку в буфер в Python 3.1?

    pandas 3×3 рассеянная матрица отсутствует ярлыки

    Как скопировать таблицу sqlite из базы данных диска в базу данных памяти в python?

    Как заменить элемент списка строк с разделителями текста на несколько элементов списка в списке python?

    Как удалить все целочисленные значения из списка в python

    Процентный шанс сделать действие

    Изменение значения точности и отсутствие изменения значения потерь в двоичной классификации с использованием Tensorflow

    swig нет модуля с именем _example

    Python - лучший язык программирования в мире.