Comillas simples vs dobles en JSON

107

Mi código:

import simplejson as json

s = "{'username':'dfdsfdsf'}" #1
#s = '{"username":"dfdsfdsf"}' #2
j = json.loads(s)

#1 la definición es incorrecta

#2 la definición es correcta

Escuché que en Python las comillas simples y dobles pueden ser intercambiables. ¿Puede alguien explicarme esto?

Bin Chen
fuente

Respuestas:

169

La sintaxis JSON no es la sintaxis de Python. JSON requiere comillas dobles para sus cadenas.

Ignacio Vázquez-Abrams
fuente
2
pero el primero es una comilla simple en JSON, estoy confundido. Ese puede pasar la compilación pero el segundo no.
Bin Chen
6
Gracias por esta confirmación. Aparentemente, soy el único que está importando str(dict)y no quiero evalhacerlo. Un simple .replace("'", '"')debería hacer el truco.
isaaclw
8
Y hablé demasiado pronto. Aparentemente es más complicado que eso.
isaaclw
6
Si necesita usar comillas dobles en todas partes, puede llamar json.dumps(..)dos veces como en: lo import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))que da:"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"
rprasad
124

puedes usar ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}
hahakubile
fuente
9
Me gusta más esta respuesta: a menudo no tienes una opción: si alguien te da comillas simples, tienes comillas simples. O json.loads necesita un argumento adicional, o debería usarlo. Reemplazar globalmente "'" es un desastre, como si los datos entrantes { 'a' : 'this "string" really isn\'t!!!!' }
fueran
@Mark, ¿se puede adaptar este método a una situación más complicada con comillas anidadas, por ejemplo "{'link':'<a href="mylink">http://my.com</a>'}"? En este caso, ast.literal_evalarroja un error de sintaxis
alancalvitti
1
Esto me parece un riesgo de seguridad.
JacksonHaenchen
2
¿Cómo responde esto a la pregunta? ¿Qué tiene esto que ver con las comillas simples o dobles en JSON? Este enfoque ast podría permitirle cargar un dictado de Python desde una cadena, pero el problema principal que tiene el OP es que la cadena # 1 no es JSON válido mientras que la cadena # 2 sí lo es.
jschultz410
43

Puede volcar JSON con comillas dobles mediante:

import json

# mixing single and double quotes
data = {'jsonKey': 'jsonValue',"title": "hello world"}

# get string with all double quotes
json_string = json.dumps(data) 
cowboybkit
fuente
12
esto va por el camino equivocado. está serializando estructuras de datos de Python a JSON; la pregunta original es sobre deserializar JSON a estructuras de datos de Python.
tedder42
5
La idea sería serializar Python en json con json.dumps, luego llamar a json.loads cuando esté en forma str.
celebrado el
3
Extrañas entender aquí. Si desea cargar una cadena json, debe estar entre comillas dobles. Lo que está haciendo es volcar json, no cadena json.
LegitMe
12

demjson también es un buen paquete para resolver el problema de la sintaxis json incorrecta:

pip install demjson

Uso:

from demjson import decode
bad_json = "{'username':'dfdsfdsf'}"
python_dict = decode(bad_json)

Editar:

demjson.decodees una gran herramienta para json dañado, pero cuando se trata de una gran cantidad de datos json ast.literal_evales una mejor coincidencia y mucho más rápido.

DhiaTN
fuente
4
demjson.decodees una gran herramienta para json dañado, pero para tareas que involucran decenas o cientos de miles de paquetes json, ast.literal_evales mucho más rápido. Por no decir demjsonque no tiene su lugar: lo uso como respaldo en caso de que fallen los métodos más rápidos.
mjwunderlich
1
En realidad, demjson funcionó mucho mejor, en lugar de probar contra ast.literal_eval y json.loads
Marware
4

Dos problemas con las respuestas dadas hasta ahora, si, por ejemplo, uno transmite ese JSON no estándar. Porque entonces uno podría tener que interpretar una cadena entrante (no un diccionario de Python).

Problema 1 - demjson: Con Python 3.7. + Y usando conda no pude instalar demjson ya que obviamente no es compatible con Python> 3.5 actualmente. Entonces necesito una solución con medios más simples, por ejemplo asty / o json.dumps.

Problema 2 - ast& json.dumps: Si un JSON tiene comillas simples y contiene una cadena en al menos un valor, que a su vez contiene comillas simples, la única solución simple pero práctica que he encontrado es aplicar ambos:

En el siguiente ejemplo asumimos que linees el objeto de cadena JSON entrante:

>>> line = str({'abc':'008565','name':'xyz','description':'can control TV\'s and more'})

Paso 1: convierta la cadena entrante en un diccionario usando ast.literal_eval()
Paso 2: aplíquela json.dumpspara la conversión confiable de claves y valores, pero sin tocar el contenido de los valores :

>>> import ast
>>> import json
>>> print(json.dumps(ast.literal_eval(line)))
{"abc": "008565", "name": "xyz", "description": "can control TV's and more"}

json.dumpspor sí solo no haría el trabajo porque no interpreta el JSON, sino que solo ve la cadena. Similar para ast.literal_eval(): aunque interpreta correctamente el JSON (diccionario), no convierte lo que necesitamos.

Siegfried Heide
fuente
3

Puedes arreglarlo de esa manera:

s = "{'username':'dfdsfdsf'}"
j = eval(s)
Robin Ali
fuente
use ast.literal_eval en lugar de eval para ayudar a evitar ataques de inyección
Simon Kingaby
2

Como se dijo, JSON no es una sintaxis de Python. Debe utilizar comillas dobles en JSON. Su creador es (in) famoso por utilizar subconjuntos estrictos de sintaxis permitida para aliviar la sobrecarga cognitiva del programador.


A continuación, puede fallar si una de las cadenas JSON en sí contiene una comilla simple como lo señaló @Jiaaro. NO UTILICE. Dejado aquí como ejemplo de lo que no funciona.

Es realmente útil saber que no hay comillas simples en una cadena JSON. Digamos que lo copió y pegó desde una consola de navegador o lo que sea. Entonces, puede escribir

a = json.loads('very_long_json_string_pasted_here')

De lo contrario, esto podría romperse si también usa comillas simples.

serv-inc
fuente
2
no es cierto que no haya comillas simples en una cadena json. Eso puede ser cierto en un caso específico, pero no puede confiar en él. por ejemplo, esto es json válido:{"key": "value 'with' single quotes"}
Jiaaro
2

Realmente resolvió mi problema usando la función eval.

single_quoted_dict_in_string = "{'key':'value', 'key2': 'value2'}"
desired_double_quoted_dict = eval(single_quoted_dict_in_string)
# Go ahead, now you can convert it into json easily
print(desired_double_quoted_dict)
Hafiz Hashim
fuente
Este es un muy mal ejemplo. ¿Qué pasa si alguien descubre que está usando eval en json y envía un código json con formato incorrecto que luego es evaluado por eval?
Metonimia
1

Recientemente me encontré con un problema muy similar y creo que mi solución también funcionaría para usted. Tenía un archivo de texto que contenía una lista de elementos en el formulario:

["first item", 'the "Second" item', "thi'rd", 'some \\"hellish\\" \'quoted" item']

Quería analizar lo anterior en una lista de Python, pero no estaba interesado en eval () ya que no podía confiar en la entrada. Primero intenté usar JSON pero solo acepta elementos entre comillas dobles, así que escribí mi propio lexer muy simple para este caso específico (solo conecte su propio "stringtoparse" y obtendrá como lista de salida: 'elementos')

#This lexer takes a JSON-like 'array' string and converts single-quoted array items into escaped double-quoted items,
#then puts the 'array' into a python list
#Issues such as  ["item 1", '","item 2 including those double quotes":"', "item 3"] are resolved with this lexer
items = []      #List of lexed items
item = ""       #Current item container
dq = True       #Double-quotes active (False->single quotes active)
bs = 0          #backslash counter
in_item = False #True if currently lexing an item within the quotes (False if outside the quotes; ie comma and whitespace)
for c in stringtoparse[1:-1]:   #Assuming encasement by brackets
    if c=="\\": #if there are backslashes, count them! Odd numbers escape the quotes...
        bs = bs + 1
        continue                    
    if (dq and c=='"') or (not dq and c=="'"):  #quote matched at start/end of an item
        if bs & 1==1:   #if escaped quote, ignore as it must be part of the item
            continue
        else:   #not escaped quote - toggle in_item
            in_item = not in_item
            if item!="":            #if item not empty, we must be at the end
                items += [item]     #so add it to the list of items
                item = ""           #and reset for the next item
            continue                
    if not in_item: #toggle of single/double quotes to enclose items
        if dq and c=="'":
            dq = False
            in_item = True
        elif not dq and c=='"':
            dq = True
            in_item = True
        continue
    if in_item: #character is part of an item, append it to the item
        if not dq and c=='"':           #if we are using single quotes
            item += bs * "\\" + "\""    #escape double quotes for JSON
        else:
            item += bs * "\\" + c
        bs = 0
        continue

Ojalá sea útil para alguien. ¡Disfrutar!

Mate
fuente
¿Qué proporciona esto que no obtiene de docs.python.org/2/library/ast.html#ast.literal_eval ?
Charles Duffy
0
import ast 
answer = subprocess.check_output(PYTHON_ + command, shell=True).strip()
    print(ast.literal_eval(answer.decode(UTF_)))

Funciona para mi

vaibhav.patil
fuente
-4
import json
data = json.dumps(list)
print(data)

El fragmento de código anterior debería funcionar.

Dheeraj R
fuente
2
Puede hacer algo útil, pero no responde a la pregunta que se hizo. El problema comienza con una cadena, no con una lista.
Rachel