Exibindo mensagem de erro melhor do que "Nenhum objeto JSON pôde ser decodificado"

128

Código Python para carregar dados de algum arquivo JSON longo e complicado:

with open(filename, "r") as f:
  data = json.loads(f.read())

(nota: a melhor versão do código deve ser:

with open(filename, "r") as f:
  data = json.load(f)

mas ambos exibem comportamento semelhante)

Para muitos tipos de erro JSON (delimitadores ausentes, barras invertidas incorretas em cadeias, etc.), isso imprime uma boa mensagem útil contendo o número da linha e da coluna onde o erro JSON foi encontrado.

No entanto, para outros tipos de erro JSON (incluindo o clássico "usando vírgula no último item de uma lista", mas também outras coisas como capitalizar verdadeiro / falso), a saída do Python é apenas:

Traceback (most recent call last):
  File "myfile.py", line 8, in myfunction
    config = json.loads(f.read())
  File "c:\python27\lib\json\__init__.py", line 326, in loads
    return _default_decoder.decode(s)
  File "c:\python27\lib\json\decoder.py", line 360, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "c:\python27\lib\json\decoder.py", line 378, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

Para esse tipo de ValueError, como você faz com que o Python diga onde está o erro no arquivo JSON?

OJW
fonte
Você poderia despejar um trecho do seu arquivo?
Ketouem
Não estou tentando encontrar o erro em um arquivo específico agora; Estou tentando modificar meu programa para destacar o erro em qualquer arquivo futuro que ele ler.
OJW 15/13
2
Não está diretamente relacionado, mas você poderia fazer em json.load(f)vez dejson.loads(f.read())
Martin Samson
@OJW em que versão do python estava esse comportamento?
jxramos 14/03
Python 3.8.1 agora fornece a posição de erro "Esperando valor: linha 1 coluna 21 (caracter 20)"
OJW

Respostas:

173

Descobri que o simplejsonmódulo fornece erros mais descritivos em muitos casos em que o jsonmódulo interno é vago. Por exemplo, no caso de uma vírgula após o último item de uma lista:

json.loads('[1,2,]')
....
ValueError: No JSON object could be decoded

o que não é muito descritivo. A mesma operação com simplejson:

simplejson.loads('[1,2,]')
...
simplejson.decoder.JSONDecodeError: Expecting object: line 1 column 5 (char 5)

Muito melhor! O mesmo acontece com outros erros comuns, como capitalização True.

tom
fonte
18
Versões futuras do Python incluirão essas melhorias; é o mesmo projeto abaixo.
Martijn Pieters
1
@ user2016290 Editar os arquivos principais / de pacote diretamente é uma má ideia. O Python é fácil de corrigir o patch, então é melhor fazer isso no código.
Rebs
2
@jxramos: O OP usou o Python 2.7, como é evidente no rastreio. Um teste rápido em ideone.com (Python 3.7.3) mostra que a jsonbiblioteca stdlib foi atualizada e fornece o novo formato de mensagem de erro. No entanto, não tenho tempo para acompanhar lançamentos exatos agora.
Martijn Pieters
1
Como o @jxramos encontrou, o Python 3.5 atualizou as exceções: bugs.python.org/issue19361 (via docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Martijn Pieters
15

Você não será capaz de obter python para dizer onde o JSON está incorreto. Você precisará usar um linter online em algum lugar como este

Isso mostrará um erro no JSON que você está tentando decodificar.

myusuf3
fonte
2
Existem ferramentas offline que podem fazer isso para arquivos JSON confidenciais?
OJW 15/13
@OJW não que eu saiba, mas isso deve resolver o problema que você está tendo ou, pelo menos, permitir que você corrija seu json quebrado.
myusuf3
12
Meu arquivo JSON está bom - estou tentando fazer com que meu programa imprima mensagens de erro úteis que sejam compreensíveis para qualquer pessoa. É bom dizer a eles que "se livrem dessa vírgula na linha 13 da coluna 32". Dizer a eles "há um erro em algum lugar do seu arquivo, faça o upload para a Internet em que as pessoas o ajudarão" é ruim.
OJW 15/13
7

Você pode experimentar a biblioteca rson encontrada aqui: http://code.google.com/p/rson/ . Também no PYPI: https://pypi.python.org/pypi/rson/0.9, para que você possa usar easy_install ou pip para obtê-lo.

para o exemplo dado por tom:

>>> rson.loads('[1,2,]')
...
rson.base.tokenizer.RSONDecodeError: Unexpected trailing comma: line 1, column 6, text ']'

O RSON foi projetado para ser um superconjunto de JSON, para que ele possa analisar arquivos JSON. Ele também tem uma sintaxe alternativa que é muito mais agradável para os humanos olharem e editarem. Eu o uso bastante para arquivos de entrada.

Quanto à capitalização de valores booleanos: parece que o rson lê booleanos incorretamente capitalizados como seqüências de caracteres.

>>> rson.loads('[true,False]')
[True, u'False']
Brad Campbell
fonte
4

Eu tive um problema semelhante e foi devido a aspas simples. O padrão JSON ( http://json.org ) fala apenas sobre o uso de aspas duplas, portanto, a jsonbiblioteca python suporta apenas aspas duplas.

Knight Samar
fonte
3

Para a minha versão específica desse problema, fui em frente e procurei a declaração da função load_json_file(path)dentro do packaging.pyarquivo e depois contrabandeei uma printlinha para ele:

def load_json_file(path):
    data = open(path, 'r').read()
    print data
    try:
        return Bunch(json.loads(data))
    except ValueError, e:
        raise MalformedJsonFileError('%s when reading "%s"' % (str(e),
                                                               path))

Dessa maneira, imprimia o conteúdo do arquivo json antes de entrar no try-catch, e dessa maneira - mesmo com meu conhecimento em Python quase inexistente - eu era capaz de descobrir rapidamente por que minha configuração não conseguia ler o arquivo json.
(Foi porque eu havia configurado meu editor de texto para escrever uma lista técnica UTF-8 ... estúpida)

Apenas mencionei isso porque, embora talvez não seja uma boa resposta para o problema específico do OP, esse foi um método bastante rápido para determinar a origem de um bug muito opressivo. E aposto que muitas pessoas tropeçarão neste artigo que estão procurando uma solução mais detalhada para a MalformedJsonFileError: No JSON object could be decoded when reading …. Então isso pode ajudá-los.

WoodrowShigeru
fonte
Você deve usar o gerenciador de contexto para o arquivo E / S ( with open(fn) as f), ele trata de fechar o arquivo em uma exceção para você. pt.wikibooks.org/wiki/Python_Programming/…
Rebs
1
+1. Se você pudesse mostrar um exemplo de seguida monkeypatching que para o comportamento padrão, isso seria muito arrumado
Craig Brett
Desculpe, nunca toquei em nenhum código Python depois que esse problema foi descoberto. Talvez outra pessoa possa ajudar?
precisa saber é o seguinte
3

Quanto a mim, meu arquivo json é muito grande, quando usado jsonem python, recebe o erro acima.

Após a instalação simplejsonpor sudo pip install simplejson.

E então eu resolvi.

import json
import simplejson


def test_parse_json():
    f_path = '/home/hello/_data.json'
    with open(f_path) as f:
        # j_data = json.load(f)      # ValueError: No JSON object could be decoded
        j_data = simplejson.load(f)  # right
    lst_img = j_data['images']['image']
    print lst_img[0]


if __name__ == '__main__':
    test_parse_json()
Jayhello
fonte
1

Eu tive um problema semelhante, este foi o meu código:

    json_file=json.dumps(pyJson)
    file = open("list.json",'w')
    file.write(json_file)  

    json_file = open("list.json","r")
    json_decoded = json.load(json_file)
    print json_decoded

o problema era que eu tinha esquecido, file.close() resolvi o problema.

Habib Kazemi
fonte
Trabalhou para mim também, não sei por que não tinha esse problema antes.
pceccon
Você deve usar o gerenciador de contexto para o arquivo E / S ( with open(fn) as f), ele trata de fechar o arquivo em uma exceção para você. pt.wikibooks.org/wiki/Python_Programming/…
Rebs
0

A resposta aceita é a mais fácil de resolver o problema. Mas, caso você não tenha permissão para instalar o simplejson devido à política da sua empresa, proponho a solução abaixo para corrigir o problema específico de "usar vírgula no último item de uma lista" :

  1. Crie uma classe filho "JSONLintCheck" para herdar da classe "JSONDecoder" e substitua o método init da classe "JSONDecoder" como abaixo:

    def __init__(self, encoding=None, object_hook=None, parse_float=None,parse_int=None, parse_constant=None, strict=True,object_pairs_hook=None)        
            super(JSONLintCheck,self).__init__(encoding=None, object_hook=None,      parse_float=None,parse_int=None, parse_constant=None, strict=True,object_pairs_hook=None)
            self.scan_once = make_scanner(self)
  1. make_scanner é uma nova função que substitui o método 'scan_once' da classe acima. E aqui está o código para ele:
  1 #!/usr/bin/env python
  2 from json import JSONDecoder
  3 from json import decoder
  4 import re
  5
  6 NUMBER_RE = re.compile(
  7     r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
  8     (re.VERBOSE | re.MULTILINE | re.DOTALL))
  9
 10 def py_make_scanner(context):
 11     parse_object = context.parse_object
 12     parse_array = context.parse_array
 13     parse_string = context.parse_string
 14     match_number = NUMBER_RE.match
 15     encoding = context.encoding
 16     strict = context.strict
 17     parse_float = context.parse_float
 18     parse_int = context.parse_int
 19     parse_constant = context.parse_constant
 20     object_hook = context.object_hook
 21     object_pairs_hook = context.object_pairs_hook
 22
 23     def _scan_once(string, idx):
 24         try:
 25             nextchar = string[idx]
 26         except IndexError:
 27             raise ValueError(decoder.errmsg("Could not get the next character",string,idx))
 28             #raise StopIteration
 29
 30         if nextchar == '"':
 31             return parse_string(string, idx + 1, encoding, strict)
 32         elif nextchar == '{':
 33             return parse_object((string, idx + 1), encoding, strict,
 34                 _scan_once, object_hook, object_pairs_hook)
 35         elif nextchar == '[':
 36             return parse_array((string, idx + 1), _scan_once)
 37         elif nextchar == 'n' and string[idx:idx + 4] == 'null':
 38             return None, idx + 4
 39         elif nextchar == 't' and string[idx:idx + 4] == 'true':
 40             return True, idx + 4
 41         elif nextchar == 'f' and string[idx:idx + 5] == 'false':
 42             return False, idx + 5
 43
 44         m = match_number(string, idx)
 45         if m is not None:
 46             integer, frac, exp = m.groups()
 47             if frac or exp:
 48                 res = parse_float(integer + (frac or '') + (exp or ''))
 49             else:
 50                 res = parse_int(integer)
 51             return res, m.end()
 52         elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
 53             return parse_constant('NaN'), idx + 3
 54         elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
 55             return parse_constant('Infinity'), idx + 8
 56         elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
 57             return parse_constant('-Infinity'), idx + 9
 58         else:
 59             #raise StopIteration   # Here is where needs modification
 60             raise ValueError(decoder.errmsg("Expecting propert name enclosed in double quotes",string,idx))
 61     return _scan_once
 62
 63 make_scanner = py_make_scanner
  1. Melhor colocar a função 'make_scanner' junto com a nova classe filho no mesmo arquivo.
Jeremy Li
fonte
0

Apenas acertei o mesmo problema e, no meu caso, o problema estava relacionado a BOM(marca de ordem de bytes) no início do arquivo.

json.tool se recusaria a processar até mesmo o arquivo vazio (apenas chaves) até eu remover a marca UTF BOM.

O que eu fiz é:

  • abri meu arquivo json com o vim,
  • marca de ordem de byte removida ( set nobomb)
  • salvar Arquivo

Isso resolveu o problema com o json.tool. Espero que isto ajude!

Tomasz W
fonte
-1

Quando seu arquivo é criado. Em vez de criar um arquivo com conteúdo está vazio. Substituir com:

json.dump({}, file)
Hoang Duong
fonte
-3

Você pode usar o cjson , que afirma ser até 250 vezes mais rápido que as implementações de python puro, já que você tem "algum arquivo JSON longo e complicado" e provavelmente precisará executá-lo várias vezes (os decodificadores falham e relatam o primeiro erro que eles apenas encontro).

yahe
fonte