Como verifico se uma string é JSON válida em Python?

184

No Python, existe uma maneira de verificar se uma string é JSON válida antes de tentar analisá-la?

Por exemplo, trabalhando com coisas como a API do Facebook Graph, às vezes retorna JSON, às vezes pode retornar um arquivo de imagem.

Joey Blake
fonte
3
a API deve definir o tipo de conteúdo
John La Rooy
4
Você não pode especificar quais dados são retornados na chamada da API? Não estou familiarizado com a API do Facebook, mas isso parece realmente estranho.
Jhocking #
Eu fiz uma vez, mas com codegolf maneira
VOCÊ
1
A maioria das respostas são json, mas, se você chamar a foto do perfil ele só retorna o jpg
Joey Blake

Respostas:

235

Você pode tentar fazer isso json.loads(), que lançará a ValueErrorse a sequência que você passar não puder ser decodificada como JSON.

Em geral, a filosofia " pitonica " para esse tipo de situação é chamada EAFP , mais fácil pedir perdão do que permissão .

John Flatness
fonte
4
Eu posso ver como isso vai funcionar. Me leva à minha próxima pergunta. Ele lança um ValueError. O que eu quero que faça neste momento é retornar a string incorreta para que eu possa fazer outra coisa com ela. Até agora, recebi apenas a mensagem de erro e o tipo.
Joey Blake
2
O que há de errado em retornar a string que você passou loadsna cláusula exceto?
John John Flatness
1
nada de errado com isso, apenas um erro noob da minha parte. Parece que eu não posso chamar file.read () duas vezes. Mas eu posso definir uma variável e usá-la. E é isso que eu fiz.
Joey Blake
5
apenas uma nota ... json.loads ('10' ) não jogar a ValueError e tenho certeza '10' não é um JSON válido ...
Wahrheit
4
Apesar do fato de que a especificação diz que um texto JSON deve ser uma matriz ou objeto, a maioria dos codificadores e decodificadores (incluindo o Python) funcionará com qualquer valor JSON no "topo", incluindo números e seqüências de caracteres. 10é um valor de número JSON válido.
perfil completo de John Flatness
145

Exemplo de script Python retorna um booleano se uma string é válida json:

import json

def is_json(myjson):
  try:
    json_object = json.loads(myjson)
  except ValueError as e:
    return False
  return True

Que imprime:

print is_json("{}")                          #prints True
print is_json("{asdf}")                      #prints False
print is_json('{ "age":100}')                #prints True
print is_json("{'age':100 }")                #prints False
print is_json("{\"age\":100 }")              #prints True
print is_json('{"age":100 }')                #prints True
print is_json('{"foo":[5,6.8],"foo":"bar"}') #prints True

Converta uma string JSON em um dicionário Python:

import json
mydict = json.loads('{"foo":"bar"}')
print(mydict['foo'])    #prints bar

mylist = json.loads("[5,6,7]")
print(mylist)
[5, 6, 7]

Converta um objeto python em string JSON:

foo = {}
foo['gummy'] = 'bear'
print(json.dumps(foo))           #prints {"gummy": "bear"}

Se você deseja acessar a análise de baixo nível, não faça o seu próprio, use uma biblioteca existente: http://www.json.org/

Ótimo tutorial sobre o módulo JSON python: https://pymotw.com/2/json/

É String JSON e mostra erros de sintaxe e mensagens de erro:

sudo cpan JSON::XS
echo '{"foo":[5,6.8],"foo":"bar" bar}' > myjson.json
json_xs -t none < myjson.json

Impressões:

, or } expected while parsing object/hash, at character offset 28 (before "bar}
at /usr/local/bin/json_xs line 183, <STDIN> line 1.

json_xs é capaz de verificar, analisar, prittificar, codificar, decodificar e muito mais:

https://metacpan.org/pod/json_xs

Eric Leschinski
fonte
Você acha que devemos del json_objectvalidar uma vez?
Akshay
4
Por que diabos não existe um método de validação adequado? Deve haver uma maneira de verificar erros sem matar canários.
Braden Best
O que estou falando é: Só porque o Python permite OO não significa que não há problema em ignorar as outras partes. Eu deveria ter a opção de A. deixar a função falhar e usar exceções (da maneira OO / Python) ou B. chamar uma função que retorne um valor (êxito ou erro) em vez de gerar uma exceção e depois ter minha função , por sua vez, retorne um valor sentinela que indica um erro, para que os erros subam na pilha de chamadas e possam ser usados ​​conforme necessário (a maneira procedural / C). Assim como o C ++ não força você a usar exceções (você pode usar errno), o Python também não deve forçá-lo
Braden Best
A validação de string JSON @BradenBest é assombrada pelo demônio que torna o problema de parada interessante. Não há uma maneira matematicamente correta de provar a correção de uma sequência, exceto para tentar sua sequência com um analisador e ver se ela termina sem erros. Para ver por que é difícil: "Escreva-me um programa que comprove que não há erros de sintaxe em um programa de computador". Isso não é possível. Os desenvolvedores de idiomas ficarão poéticos sobre a eterna corrida armamentista de codificação e decodificação. O melhor que podemos fazer é retornar yes / no se uma string for válida para um determinado mecanismo, não para todos os mecanismos possíveis.
Eric Leschinski
1
@EricLeschinski, mas não há um problema de parada aqui. O programa gera claramente uma exceção se ocorrer um erro ao analisar o JSON. Portanto, o programa sabe quando a entrada JSON é inválida. Portanto, é 100% possível ter uma função que verifique se a entrada é válida sem precisar usar try. #StopCanaryAbuse
Braden Best
2

Eu diria que analisar é a única maneira que você pode realmente dizer inteiramente. A exceção será gerada pela json.loads()função do python (quase certamente), se não o formato correto. No entanto, para os fins do seu exemplo, você provavelmente pode apenas verificar os dois primeiros caracteres que não são de espaço em branco ...

Eu não estou familiarizado com o JSON que o facebook envia de volta, mas a maioria das strings JSON de aplicativos da web começa com um quadrado [ou {colchete aberto . Não conheço nenhum formato de imagem que comece com esses caracteres.

Por outro lado, se você souber quais formatos de imagem podem aparecer, verifique o início da sequência de caracteres para identificar as imagens e assumir que possui JSON, se não for uma imagem.

Outro truque simples para identificar um gráfico, em vez de uma sequência de texto, no caso de você procurar um gráfico, é apenas testar caracteres não ASCII nas primeiras dezenas de caracteres da string (assumindo que o JSON é ASCII )

Tim
fonte
0

Eu vim com uma solução genérica e interessante para esse problema:

class SafeInvocator(object):
    def __init__(self, module):
        self._module = module

    def _safe(self, func):
        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except:
                return None

        return inner

    def __getattr__(self, item):
        obj = getattr(self.module, item)
        return self._safe(obj) if hasattr(obj, '__call__') else obj

e você pode usá-lo assim:

safe_json = SafeInvocator(json)
text = "{'foo':'bar'}"
item = safe_json.loads(text)
if item:
    # do something
odedlaz
fonte
1
Penso que soluções gerais são boas, mas, neste caso, a exceptcláusula pode ocultar qualquer exceção séria. A captura de exceções deve ser o mais restritiva possível.
Lucastamoios