Aspas simples ou duplas em JSON

108

Meu código:

import simplejson as json

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

#1 definição está errada

#2 definição está certa

Ouvi dizer que no Python essas aspas simples e duplas podem ser intercambiáveis. Alguém pode me explicar isso?

Bin Chen
fonte

Respostas:

170

A sintaxe JSON não é sintaxe Python. JSON requer aspas duplas para suas strings.

Ignacio Vazquez-Abrams
fonte
2
mas primeiro é aspas simples em JSON, estou confuso. Aquele pode passar de compilação, mas o segundo não.
Bin Chen
6
Obrigado por esta confirmação. Aparentemente sou o único importando str(dict)e não quero eval. Um simples .replace("'", '"')deve resolver o problema.
isaaclw
8
E eu falei muito cedo. Aparentemente, é mais complicado do que isso.
isaaclw
6
Se precisar usar aspas duplas, você pode ligar json.dumps(..)duas vezes como em: o import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))que dá:"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"
rprasad
124

você pode usar ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}
hahakubile
fonte
9
Eu gosto mais desta resposta: muitas vezes você não tem escolha: se alguém lhe dá aspas simples, você tem aspas simples. Ou json.loads precisa de um argumento extra ou você deve usá-lo. Substituir globalmente "'" é um desastre, como se os dados recebidos { 'a' : 'this "string" really isn\'t!!!!' }
fossem
@Mark, este método pode ser adaptado a uma situação mais complicada com aspas aninhadas, por exemplo "{'link':'<a href="mylink">http://my.com</a>'}"? Nesse caso, ast.literal_evalgera um erro de sintaxe
alancalvitti
1
Isso parece um risco de segurança para mim.
JacksonHaenchen
2
Como isso responde à pergunta? O que isso tem a ver com aspas simples e duplas em JSON? Essa abordagem ast pode permitir que você carregue um dicionário Python de uma string, mas o principal problema do OP é que a string # 1 não é um JSON válido, enquanto a string # 2 é.
jschultz410
43

Você pode descartar JSON com aspas duplas:

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
fonte
12
isso vai para o lado errado. você está serializando estruturas de dados Python para JSON; a pergunta original é sobre desserializar JSON para estruturas de dados python.
tedder42
5
A ideia seria serializar o python em json com json.dumps e, em seguida, chamar json.loads quando estiver na forma str.
realizado em
3
Você sente falta de entender aqui. Se você deseja carregar a string json, ela deve estar com aspas duplas. O que você está fazendo ainda é despejar json, não string json.
LegitMe de
12

demjson também é um bom pacote para resolver o problema de sintaxe json inválida:

pip install demjson

Uso:

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

Editar:

demjson.decodeé uma ótima ferramenta para json danificado, mas quando você está lidando com uma grande área de dados json ast.literal_evalé uma combinação melhor e muito mais rápida.

DhiaTN
fonte
4
demjson.decodeé uma ótima ferramenta para json danificado - mas para tarefas que envolvem dezenas ou centenas de milhares de pacotes json, ast.literal_evalé muito mais rápido. Para não dizer demjsonque não tem o seu lugar: eu o uso como fallback caso métodos mais rápidos falhem.
mjwunderlich de
1
Na verdade, o demjson funcionou muito melhor, em vez de testar contra ast.literal_eval e json.loads
Marware
4

Dois problemas com as respostas dadas até agora, se, por exemplo, um transmitir tal JSON não padrão. Porque então pode ser necessário interpretar uma string de entrada (não um dicionário python).

Problema 1 - demjson: Com Python 3.7. + E usando conda, não consegui instalar o demjson, pois obviamente ele não oferece suporte a Python> 3.5 atualmente. Portanto, preciso de uma solução com meios mais simples, por exemplo aste / ou json.dumps.

Problema 2 - ast& json.dumps: Se um JSON tem aspas simples e contém uma string em pelo menos um valor, que por sua vez contém aspas simples, a única solução simples, porém prática, que encontrei é aplicar ambos:

No exemplo a seguir, supomos que lineseja o objeto de string JSON de entrada:

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

Etapa 1: converta a string de entrada em um dicionário usando a ast.literal_eval()
Etapa 2: aplique json.dumpsa ela para a conversão confiável de chaves e valores, mas sem tocar no conteúdo dos valores :

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

json.dumpssozinho não faria o trabalho porque não interpreta o JSON, mas apenas vê a string. Semelhante para ast.literal_eval(): embora interprete corretamente o JSON (dicionário), ele não converte o que precisamos.

Siegfried Heide
fonte
3

Você pode consertar assim:

s = "{'username':'dfdsfdsf'}"
j = eval(s)
Robin Ali
fonte
use ast.literal_eval em vez de eval para ajudar a evitar ataques de injeção
Simon Kingaby
2

Como disse, JSON não é sintaxe Python. Você precisa usar aspas duplas em JSON. Seu criador é (in) famoso por usar subconjuntos estritos de sintaxe permitida para facilitar a sobrecarga cognitiva do programador.


Abaixo pode falhar se uma das strings JSON contiver uma aspa simples, conforme apontado por @Jiaaro. NÃO USE. Deixado aqui como exemplo do que não funciona.

É muito útil saber que não há aspas simples em uma string JSON. Digamos, você copiou e colou de um console de navegador / qualquer coisa. Então, você pode simplesmente digitar

a = json.loads('very_long_json_string_pasted_here')

Caso contrário, isso poderia falhar se também usasse aspas simples.

serv-inc
fonte
2
não é verdade que não há aspas simples em uma string json. Isso pode ser verdade em um caso específico, mas você não pode confiar nisso. por exemplo, este é um json válido:{"key": "value 'with' single quotes"}
Jiaaro
2

Ele realmente resolveu meu problema usando a função 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
fonte
Este é um péssimo exemplo. E se alguém descobrir que você está usando eval em json e enviar um json malformado contendo código que será avaliado por eval?
Metonímia
1

Recentemente me deparei com um problema muito semelhante e acredito que minha solução funcionaria para você também. Eu tinha um arquivo de texto que continha uma lista de itens no formulário:

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

Eu queria analisar o acima em uma lista python, mas não estava interessado em eval () porque não podia confiar na entrada. Tentei primeiro usar JSON, mas ele só aceita itens com aspas duplas, então escrevi meu próprio lexer muito simples para este caso específico (basta conectar seu próprio "stringtoparse" e você obterá como lista de saída: 'itens')

#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

Espero que seja útil para alguém. Aproveitar!

Matt
fonte
O que isso fornece você não obtém em 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 mim

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

O trecho de código acima deve funcionar.

Dheeraj R
fonte
2
Pode fazer algo útil, mas não responde à pergunta que foi feita. O problema começa com uma string, não uma lista.
Rachel