¿Cómo verifico si una cadena es JSON válida en Python?

184

En Python, ¿hay alguna manera de verificar si una cadena es JSON válida antes de intentar analizarla?

Por ejemplo, trabajando con cosas como la API Graph de Facebook, a veces devuelve JSON, a veces puede devolver un archivo de imagen.

Joey Blake
fuente
3
la API debería establecer el tipo de contenido
John La Rooy
44
¿No puede especificar qué datos se devuelven en la llamada API? No estoy familiarizado con la API de Facebook, pero eso suena realmente extraño.
jhocking 01 de
Lo he hecho una vez, pero con la forma de codegolf
USTED
1
La mayoría de las respuestas son json, pero, si llamas a la foto de perfil, solo devuelve el jpg
Joey Blake

Respuestas:

235

Puede intentar hacerlo json.loads(), lo que arrojará un ValueErrorsi la cadena que pasa no se puede decodificar como JSON.

En general, la filosofía " pitónica " para este tipo de situación se llama EAFP , por más fácil pedir perdón que permiso .

John Flatness
fuente
44
Puedo ver cómo funcionará eso. Me lleva a mi siguiente pregunta. Lanza un ValueError. Lo que quiero que haga en este punto es devolver la cadena ofensiva para que pueda hacer algo más con ella. Hasta ahora, solo recibí el mensaje de error y el tipo.
Joey Blake
2
¿Qué hay de malo en devolver la cadena a la que pasaste loadsen la cláusula except?
John Flatness
1
no tiene nada de malo, solo un error novato de mi parte. Parece que no puedo llamar a file.read () dos veces. Pero puedo establecer una variable y usarla. Y eso fue lo que hice.
Joey Blake
55
solo una nota ... json.loads ('10 ') no arroja el ValueError y estoy seguro de que' 10 'no es un json válido ...
wahrheit
44
A pesar de que la especificación dice que un texto JSON debe ser una matriz u objeto, la mayoría de los codificadores y decodificadores (incluido Python) funcionarán con cualquier valor JSON en la "parte superior", incluidos los números y las cadenas. 10es un valor de número JSON válido.
John Flatness
145

La secuencia de comandos de Python de ejemplo devuelve un valor booleano si una cadena es json válida:

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

Convierta una cadena JSON en un diccionario de Python:

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

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

Convierta un objeto de Python a una cadena JSON:

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

Si desea acceder a análisis de bajo nivel, no utilice el suyo propio, use una biblioteca existente: http://www.json.org/

Gran tutorial sobre el módulo Python JSON: https://pymotw.com/2/json/

Es String JSON y muestra errores de sintaxis y mensajes de error:

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

Huellas dactilares:

, 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 es capaz de verificar, analizar, prittificar, codificar, decodificar y más sintaxis:

https://metacpan.org/pod/json_xs

Eric Leschinski
fuente
¿Crees que del json_objectalguna vez deberíamos validar?
Akshay
44
¿Por qué demonios no hay un método de validación adecuado? Debería haber una forma de verificación de errores sin matar a los canarios.
Braden Best
Lo que quiero decir es: el hecho de que Python permita OO no significa que esté bien ignorar las otras partes. Debería tener la opción de A. dejar que la función falle y usar excepciones (la forma OO / Python), o B. llamar a una función que devuelve un valor (éxito o error) en lugar de lanzar una excepción, y luego tener mi función , a su vez, devuelve un valor centinela que indica un error, de modo que los errores burbujean la pila de llamadas y se pueden usar según sea necesario (la forma de procedimiento / C). Así como C ++ no lo obliga a usar excepciones (puede usar errno), Python tampoco debería forzarlo
Braden Best
La validación de cadena @BradenBest JSON es perseguida por el demonio que hace que el problema de detención sea interesante. No hay una forma matemáticamente correcta de demostrar la corrección de una cadena, excepto probar su cadena con un analizador y ver si termina sin errores. Para ver por qué es difícil: "Escríbeme un programa que pruebe que no existen errores de sintaxis en un programa de computadora". Eso no es posible. Los desarrolladores de lenguaje se volverán poéticos sobre la eterna carrera armamentista de codificación y decodificación. Lo mejor que podemos hacer es devolver sí / no si una cadena es válida para un motor dado, no para todos los motores posibles.
Eric Leschinski
1
@EricLeschinski pero no hay un problema de detención aquí. El programa claramente genera una excepción si se produce un error al analizar JSON. Por lo tanto, el programa sabe cuándo la entrada JSON no es válida. Por lo tanto, es 100% posible tener una función que verifique si la entrada es válida sin tener que usarla try. #StopCanaryAbuse
Braden Mejor
2

Yo diría que analizarlo es la única forma en que realmente puedes decirlo por completo. La json.loads()función de Python generará una excepción (casi con certeza) si no es el formato correcto. Sin embargo, con el propósito de su ejemplo, probablemente pueda verificar los primeros caracteres que no son espacios en blanco ...

No estoy familiarizado con el JSON que Facebook envía, pero la mayoría de las cadenas JSON de aplicaciones web comenzarán con un corchete abierto [o cuadrado {. No se conocen formatos de imágenes que comiencen con esos personajes.

Por el contrario, si sabe qué formatos de imagen pueden aparecer, puede verificar el inicio de la cadena para sus firmas para identificar imágenes, y asumir que tiene JSON si no es una imagen.

Otro truco simple para identificar un gráfico, en lugar de una cadena de texto, en el caso de que esté buscando un gráfico, es probar los caracteres no ASCII en las primeras dos docenas de caracteres de la cadena (suponiendo que el JSON sea ASCII )

Tim
fuente
0

Se me ocurrió una solución genérica e interesante para este 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

y puedes usarlo así:

safe_json = SafeInvocator(json)
text = "{'foo':'bar'}"
item = safe_json.loads(text)
if item:
    # do something
Odedlaz
fuente
1
Creo que las soluciones generales son buenas, pero en este caso, la exceptcláusula puede ocultar cualquier excepción seria. Las excepciones de captura deben ser lo más restrictivas posible.
lucastamoios