Kutipan tunggal vs ganda di JSON

108

Kode saya:

import simplejson as json

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

#1 definisi salah

#2 definisi benar

Saya mendengar bahwa dengan Python, kutipan tunggal dan ganda dapat dipertukarkan. Adakah yang bisa menjelaskan ini kepada saya?

Bin Chen
sumber

Jawaban:

170

Sintaks JSON bukan sintaks Python. JSON membutuhkan tanda kutip ganda untuk stringnya.

Ignacio Vazquez-Abrams
sumber
2
tapi yang pertama itu kutipan tunggal di JSON, saya bingung. Yang satu bisa lolos kompilasi tapi yang kedua tidak bisa.
Bin Chen
6
Terima kasih atas konfirmasi ini Rupanya saya satu-satunya yang mengimpor str(dict), dan tidak menginginkannya eval. Yang sederhana .replace("'", '"')harus melakukan triknya.
isaaclw
8
Dan saya berbicara terlalu cepat. Ternyata lebih rumit dari itu.
isaaclw
6
Jika Anda perlu menggunakan tanda kutip ganda di sekeliling, Anda dapat memanggil json.dumps(..)dua kali seperti di: import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))yang memberikan:"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"
rprasad
124

kamu bisa memakai ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}
hahakubile
sumber
9
Saya paling suka jawaban ini: Anda tidak sering punya pilihan: jika seseorang memberi Anda tanda kutip tunggal, Anda mendapat tanda kutip tunggal. Baik json.loads memerlukan argumen tambahan, atau Anda harus menggunakan ini. Mengganti "'" secara global adalah bencana, karena jika data yang masuk adalah:{ 'a' : 'this "string" really isn\'t!!!!' }
Mark Gerolimatos
@ Mark, dapatkah metode ini disesuaikan dengan situasi yang lebih rumit dengan kutipan bertingkat misalnya "{'link':'<a href="mylink">http://my.com</a>'}"? Dalam hal ini, menampilkan ast.literal_evalkesalahan sintaks
alancalvitti
1
Ini sepertinya risiko keamanan bagi saya.
JacksonHaenchen
2
Bagaimana ini menjawab pertanyaan? Apa hubungannya hal ini dengan tanda kutip tunggal vs. ganda di JSON? Pendekatan ast ini dapat memungkinkan Anda memuat dikt Python dari sebuah string, tetapi masalah utama yang dimiliki OP adalah bahwa string # 1 bukanlah JSON yang valid sedangkan string # 2 adalah.
jschultz410
43

Anda dapat membuang JSON dengan kutipan ganda dengan:

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
sumber
12
ini salah jalan. Anda membuat serialisasi struktur data python ke JSON; pertanyaan aslinya adalah tentang deserialisasi JSON ke struktur data python.
tedder42
5
Idenya adalah untuk menserialisasi python menjadi json dengan json.dumps, lalu memanggil json.loads di atasnya ketika itu dalam bentuk str.
Jheld
3
Anda rindu mengerti di sini. Jika Anda ingin memuat string json, itu harus berupa petik ganda. Apa yang Anda lakukan masih membuang json, bukan string json.
LegitMe
12

demjson juga merupakan paket yang bagus untuk mengatasi masalah sintaks json yang buruk:

pip install demjson

Pemakaian:

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

Edit:

demjson.decodeadalah alat yang hebat untuk json yang rusak, tetapi ketika Anda berurusan dengan sejumlah besar data json ast.literal_evaladalah cara yang lebih baik dan lebih cepat.

DhiaTN
sumber
4
demjson.decodeadalah alat yang hebat untuk json yang rusak - tetapi untuk tugas yang melibatkan puluhan atau ratusan ribu paket json, ast.literal_evaljauh lebih cepat. Bukan untuk mengatakan demjsontidak memiliki tempatnya: Saya menggunakannya sebagai cadangan jika metode yang lebih cepat gagal.
mjwunderlich
1
Sebenarnya demjson adalah salah satu yang bekerja jauh lebih baik, daripada menguji terhadap ast.literal_eval dan json.loads
Marware
4

Dua masalah dengan jawaban yang diberikan sejauh ini, jika, misalnya, seseorang mengalirkan JSON yang tidak standar tersebut. Karena dengan demikian seseorang mungkin harus menafsirkan string yang masuk (bukan kamus python).

Masalah 1 - demjson: Dengan Python 3.7. + Dan menggunakan conda saya tidak dapat menginstal demjson karena jelas tidak mendukung Python> 3.5 saat ini. Jadi saya butuh solusi dengan cara yang lebih sederhana, misalnya astdan / atau json.dumps.

Masalah 2 - ast& json.dumps: Jika JSON adalah kutipan tunggal dan berisi string setidaknya dalam satu nilai, yang pada gilirannya berisi tanda kutip tunggal, satu-satunya solusi sederhana namun praktis yang saya temukan adalah menerapkan keduanya:

Dalam contoh berikut kami asumsikan lineadalah objek string JSON yang masuk:

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

Langkah 1: ubah string yang masuk menjadi kamus menggunakan ast.literal_eval()
Langkah 2: terapkan json.dumpspadanya untuk konversi kunci dan nilai yang dapat diandalkan, tetapi tanpa menyentuh konten nilai :

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

json.dumpssaja tidak akan melakukan pekerjaan itu karena tidak menafsirkan JSON, tetapi hanya melihat stringnya. Serupa untuk ast.literal_eval(): meskipun menafsirkan JSON (kamus) dengan benar, itu tidak mengubah apa yang kita butuhkan.

Siegfried Heide
sumber
3

Anda bisa memperbaikinya seperti itu:

s = "{'username':'dfdsfdsf'}"
j = eval(s)
Robin Ali
sumber
gunakan ast.literal_eval daripada eval untuk membantu menghindari serangan injeksi
Simon Kingaby
2

Seperti yang dikatakan, JSON bukanlah sintaks Python. Anda perlu menggunakan tanda kutip ganda di JSON. Penciptanya (di-) terkenal karena menggunakan subset ketat dari sintaks yang diizinkan untuk meringankan beban kognitif programmer.


Di bawah ini bisa gagal jika salah satu string JSON itu sendiri berisi satu kutipan seperti yang ditunjukkan oleh @Jiaaro. JANGAN GUNAKAN. Kiri di sini sebagai contoh apa yang tidak berhasil.

Sangat berguna untuk mengetahui bahwa tidak ada tanda kutip tunggal dalam string JSON. Katakanlah, Anda menyalin dan menempelkannya dari konsol browser / apa pun. Kemudian, Anda tinggal mengetik

a = json.loads('very_long_json_string_pasted_here')

Ini mungkin rusak jika menggunakan tanda kutip tunggal juga.

serv-inc
sumber
2
tidak benar bahwa tidak ada tanda kutip tunggal dalam string json. Itu mungkin benar dalam kasus tertentu, tetapi Anda tidak dapat mengandalkannya. Misalnya, ini berlaku json:{"key": "value 'with' single quotes"}
Jiaaro
2

Ini benar-benar memecahkan masalah saya menggunakan fungsi 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
sumber
Ini adalah contoh yang sangat buruk. Bagaimana jika seseorang mengetahui Anda menggunakan eval pada json dan mengirimkan kode json yang salah format yang kemudian dievaluasi oleh eval?
Metonymy
1

Saya baru-baru ini menghadapi masalah yang sangat mirip, dan yakin solusi saya juga akan berhasil untuk Anda. Saya memiliki file teks yang berisi daftar item dalam bentuk:

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

Saya ingin mengurai di atas menjadi daftar python tetapi tidak tertarik pada eval () karena saya tidak bisa mempercayai inputnya. Saya mencoba pertama kali menggunakan JSON tetapi hanya menerima item yang dikutip ganda, jadi saya menulis lexer saya sendiri yang sangat sederhana untuk kasus khusus ini (cukup colokkan "stringtoparse" Anda sendiri dan Anda akan mendapatkan daftar keluaran: 'item')

#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

Semoga bermanfaat bagi seseorang. Nikmati!

Matt
sumber
Apa yang tidak Anda dapatkan dari 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_)))

Bekerja untuk saya

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

Potongan kode di atas harus berfungsi.

Dheeraj R
sumber
2
Ini mungkin melakukan sesuatu yang berguna, tapi tidak menjawab pertanyaan yang ditanyakan. Masalahnya dimulai dengan string, bukan daftar.
Rachel