Guillemets simples ou doubles en JSON

108

Mon code:

import simplejson as json

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

#1 la définition est fausse

#2 la définition est juste

J'ai entendu dire qu'en Python , les guillemets simples et doubles peuvent être interchangeables. Quelqu'un peut-il m'expliquer cela?

Bin Chen
la source

Réponses:

170

La syntaxe JSON n'est pas la syntaxe Python. JSON nécessite des guillemets doubles pour ses chaînes.

Ignacio Vazquez-Abrams
la source
2
mais d'abord, c'est un guillemet simple en JSON, je suis confus. Celui-là peut passer à la compilation, mais pas le second.
Bin Chen
6
Merci pour cette confirmation. Apparemment, je suis le seul à importer str(dict), et je ne le veux pas eval. Un simple .replace("'", '"')devrait faire l'affaire.
isaaclw
8
Et j'ai parlé trop tôt. Apparemment, c'est plus compliqué que ça.
isaaclw
6
Si vous avez besoin d'utiliser des guillemets tout autour, vous pouvez appeler json.dumps(..)deux fois comme dans: import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))ce qui donne:"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"
rprasad
124

vous pouvez utiliser ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}
hahakubile
la source
9
J'aime bien cette réponse: vous n'avez pas souvent le choix: si quelqu'un vous donne des guillemets simples, vous avez des guillemets simples. Soit json.loads a besoin d'un argument supplémentaire, soit vous devez l'utiliser. Remplacer globalement "'" est un désastre, comme si les données entrantes { 'a' : 'this "string" really isn\'t!!!!' }
étaient
@Mark, cette méthode peut-elle être adaptée à une situation plus délicate avec des guillemets imbriqués par exemple "{'link':'<a href="mylink">http://my.com</a>'}"? Dans ce cas, ast.literal_evallance une erreur de syntaxe
alancalvitti
1
Cela me semble être un risque pour la sécurité.
JacksonHaenchen
2
Comment cela répond-il à la question? Qu'est-ce que cela a à voir avec les guillemets simples ou doubles dans JSON? Cette astuce pourrait vous permettre de charger un dict Python à partir d'une chaîne, mais le principal problème de l'OP est que la chaîne n ° 1 n'est pas un JSON valide alors que la chaîne n ° 2 l'est.
jschultz410 le
43

Vous pouvez vider JSON avec des guillemets doubles par:

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
la source
12
cela va dans le mauvais sens. vous sérialisez des structures de données python en JSON; la question initiale concerne la désérialisation de JSON en structures de données python.
tedder42
5
L'idée serait de sérialiser le python en json avec json.dumps, puis d'appeler json.loads dessus quand il est sous la forme str.
publié le
3
Vous manquez de comprendre ici. Si vous voulez charger la chaîne json, il doit y avoir des guillemets doubles. Ce que vous faites est toujours de vider json, pas de chaîne json.
LegitMe
12

demjson est également un bon package pour résoudre le problème de la mauvaise syntaxe json:

pip install demjson

Usage:

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

Éditer:

demjson.decodeest un excellent outil pour json endommagé, mais lorsque vous avez affaire à une grande quantité de données json, ast.literal_evalc'est une meilleure correspondance et beaucoup plus rapide.

DhiaTN
la source
4
demjson.decodeest un excellent outil pour json endommagé - mais pour les tâches impliquant des dizaines ou des centaines de milliers de paquets json, ast.literal_evalc'est beaucoup plus rapide. Pour demjsonne pas dire n'a pas sa place: je l'utilise comme solution de secours en cas d'échec des méthodes plus rapides.
mjwunderlich
1
En fait, demjson fonctionne beaucoup mieux, au lieu de tester contre ast.literal_eval et json.loads
Marware
4

Deux problèmes avec les réponses données jusqu'à présent, si, par exemple, on diffuse un tel JSON non standard. Car alors on pourrait avoir à interpréter une chaîne entrante (pas un dictionnaire python).

Problème 1 - demjson: Avec Python 3.7. + Et en utilisant conda, je n'ai pas pu installer demjson car il ne prend évidemment pas en charge Python> 3.5 actuellement. J'ai donc besoin d'une solution avec des moyens plus simples, par exemple astet / ou json.dumps.

Problème 2 - ast& json.dumps: Si un JSON est à la fois entre guillemets simples et contient une chaîne dans au moins une valeur, qui à son tour contient des guillemets simples, la seule solution simple mais pratique que j'ai trouvée est d'appliquer les deux:

Dans l'exemple suivant, nous supposons qu'il lines'agit de l'objet de chaîne JSON entrant:

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

Étape 1: convertissez la chaîne entrante en dictionnaire en utilisant ast.literal_eval()
Étape 2: appliquez- json.dumpsy pour la conversion fiable des clés et des valeurs, mais sans toucher au contenu des valeurs :

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

json.dumpsseul ne ferait pas le travail car il n'interprète pas le JSON, mais ne voit que la chaîne. Similaire pour ast.literal_eval(): bien qu'il interprète correctement le JSON (dictionnaire), il ne convertit pas ce dont nous avons besoin.

Siegfried Heide
la source
3

Vous pouvez le réparer de cette façon:

s = "{'username':'dfdsfdsf'}"
j = eval(s)
Robin Ali
la source
utilisez ast.literal_eval au lieu de eval pour éviter les attaques par injection
Simon Kingaby
2

Comme dit, JSON n'est pas une syntaxe Python. Vous devez utiliser des guillemets doubles dans JSON. Son créateur est (in) célèbre pour avoir utilisé des sous-ensembles stricts de syntaxe autorisée pour soulager la surcharge cognitive du programmeur.


Ci-dessous peut échouer si l'une des chaînes JSON elle-même contient un guillemet simple comme indiqué par @Jiaaro. NE PAS UTILISER. Laissé ici comme exemple de ce qui ne fonctionne pas.

C'est vraiment utile de savoir qu'il n'y a pas de guillemets simples dans une chaîne JSON. Dites, vous l'avez copié et collé à partir d'une console de navigateur / peu importe. Ensuite, vous pouvez simplement taper

a = json.loads('very_long_json_string_pasted_here')

Sinon, cela pourrait également être interrompu s'il utilisait des guillemets simples.

serv-inc
la source
2
ce n'est pas vrai qu'il n'y a pas de guillemets simples dans une chaîne json. Cela peut être vrai dans un cas particulier, mais vous ne pouvez pas vous y fier. par exemple, ceci est valide json:{"key": "value 'with' single quotes"}
Jiaaro
2

Cela a vraiment résolu mon problème en utilisant la fonction 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
la source
C'est un très mauvais exemple. Que faire si quelqu'un découvre que vous utilisez eval sur json et envoie un code json malformé qui est ensuite évalué par eval?
Métonymie le
1

Je me suis récemment heurté à un problème très similaire et je crois que ma solution fonctionnerait aussi pour vous. J'avais un fichier texte qui contenait une liste d'éléments sous la forme:

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

Je voulais analyser ce qui précède dans une liste python mais je n'étais pas intéressé par eval () car je ne pouvais pas faire confiance à l'entrée. J'ai d'abord essayé d'utiliser JSON mais il n'accepte que les éléments entre guillemets, j'ai donc écrit mon propre lexer très simple pour ce cas spécifique (branchez simplement votre propre "stringtoparse" et vous obtiendrez comme liste de sortie: 'items')

#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

Espérons que cela soit utile à quelqu'un. Prendre plaisir!

Mat
la source
Qu'est-ce que cela ne vous apporte pas de docs.python.org/2/library/ast.html#ast.literal_eval ?
Charles Duffy
-1
import ast 
answer = subprocess.check_output(PYTHON_ + command, shell=True).strip()
    print(ast.literal_eval(answer.decode(UTF_)))

Travaille pour moi

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

L'extrait de code ci-dessus devrait fonctionner.

Dheeraj R
la source
2
Cela peut faire quelque chose d'utile, mais cela ne répond pas à la question qui a été posée. Le problème commence par une chaîne, pas une liste.
Rachel