คำพูดเดี่ยวและคู่ใน JSON


118

รหัสของฉัน:

import simplejson as json

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

#1 นิยามผิด

#2 คำจำกัดความถูกต้อง

ผมได้ยินมาว่าในหลามที่เดียวและคู่อ้างสามารถแทนกัน ใครช่วยอธิบายเรื่องนี้ให้ฉันฟังหน่อย

คำตอบ:


179

ไวยากรณ์ JSONไม่ใช่ไวยากรณ์ Python JSON ต้องการเครื่องหมายคำพูดคู่สำหรับสตริง


2
แต่อย่างแรกมันเป็นคำพูดเดียวใน JSON ฉันสับสน อันนั้นสามารถคอมไพล์ได้ แต่อันที่สองทำไม่ได้
Bin Chen

7
ขอบคุณสำหรับการยืนยันนี้ เห็นได้ชัดว่าผมเป็นเพียงหนึ่งในการนำเข้าstr(dict)และไม่ต้องการที่จะevalมัน .replace("'", '"')เคล็ดลับง่ายๆควรทำ
isaaclw

8
และฉันก็พูดเร็วเกินไป เห็นได้ชัดว่ามันซับซ้อนกว่านั้น
isaaclw

6
หากคุณต้องการใช้เครื่องหมายคำพูดคู่รอบ ๆ คุณสามารถเรียกjson.dumps(..)สองครั้งได้ใน: import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))ซึ่งให้:"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"
rprasad

129

คุณสามารถใช้ได้ ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}

9
ฉันชอบคำตอบนี้ที่สุด: คุณมักไม่มีทางเลือก: ถ้ามีคนให้คำพูดเดียวคุณจะมีคำพูดเดียว json.loads ต้องการอาร์กิวเมนต์เพิ่มเติมหรือคุณควรใช้สิ่งนี้ การแทนที่ "'" ทั่วโลกถือเป็นหายนะเช่นเดียวกับข้อมูลที่เข้ามา:{ 'a' : 'this "string" really isn\'t!!!!' }
Mark Gerolimatos

@ มาร์ควิธีนี้สามารถปรับให้เข้ากับสถานการณ์ที่ยุ่งยากกว่าด้วยอัญประกาศแบบซ้อนได้"{'link':'<a href="mylink">http://my.com</a>'}"หรือไม่? ในกรณีนี้แสดงast.literal_evalข้อผิดพลาดทางไวยากรณ์
alancalvitti

2
ดูเหมือนว่าจะเป็นความเสี่ยงด้านความปลอดภัยสำหรับฉัน
JacksonHaenchen

2
สิ่งนี้ตอบคำถามได้อย่างไร? สิ่งนี้เกี่ยวข้องกับเครื่องหมายคำพูดเดี่ยวกับคู่ใน JSON อย่างไร วิธี ast นี้ช่วยให้คุณโหลด Python dict จากสตริงได้ แต่ปัญหาหลักที่ OP มีคือสตริง # 1 ไม่ใช่ JSON ที่ถูกต้องในขณะที่สตริง # 2 คือ
jschultz410

ast.literal_evalไม่ใช่ความเสี่ยงด้านความปลอดภัย แต่evalอย่างใด
bone225

47

คุณสามารถถ่ายโอนข้อมูล JSON ด้วยเครื่องหมายคำพูดคู่โดย:

import json

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

# get string with all double quotes
json_string = json.dumps(data) 

13
นี่ไปผิดทาง คุณกำลังจัดลำดับโครงสร้างข้อมูล python เป็น JSON คำถามเดิมเกี่ยวกับการแยกโครงสร้างข้อมูล JSON เป็น python
tedder42

5
แนวคิดคือการทำให้ python เป็นอนุกรมเป็น json ด้วย json.dumps จากนั้นเรียก json.loads เมื่ออยู่ในรูปแบบ str
ก.ค.

3
คุณพลาดเข้าใจที่นี่ ถ้าจะโหลด json string จะต้องมี double quote สิ่งที่คุณทำยังคงเป็น dump json ไม่ใช่สตริง json
LegitMe

12

demjsonยังเป็นแพ็คเกจที่ดีในการแก้ปัญหาไวยากรณ์ json ที่ไม่ดี:

pip install demjson

การใช้งาน:

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

แก้ไข:

demjson.decodeเป็นเครื่องมือที่ยอดเยี่ยมสำหรับ json ที่เสียหาย แต่เมื่อคุณจัดการกับข้อมูล json จำนวนมากast.literal_evalจะเป็นการจับคู่ที่ดีกว่าและเร็วกว่ามาก


4
demjson.decodeเป็นเครื่องมือที่ยอดเยี่ยมสำหรับ json ที่เสียหาย แต่สำหรับงานที่เกี่ยวข้องกับแพ็กเก็ต json นับหมื่นหรือหลายแสนast.literal_evalจะเร็วกว่ามาก ไม่ต้องบอกว่าdemjsonไม่มีที่: ฉันใช้เป็นทางเลือกในกรณีที่วิธีการที่เร็วกว่าล้มเหลว
mjwunderlich

1
จริงๆแล้ว demjson นั้นทำงานได้ดีกว่ามากแทนที่จะทดสอบกับ ast.literal_eval และ json.loads
Marware

7

มีปัญหาสองข้อกับคำตอบที่ได้รับตัวอย่างเช่นหากมีรายการหนึ่งสตรีม JSON ที่ไม่ได้มาตรฐานดังกล่าว เพราะงั้นอาจต้องตีความสตริงขาเข้า (ไม่ใช่พจนานุกรม python)

ปัญหาที่ 1 - demjson: ด้วย Python 3.7 + และการใช้ conda ฉันไม่สามารถติดตั้ง demjson ได้เนื่องจากในปัจจุบันไม่รองรับ Python> 3.5 ดังนั้นฉันต้องการวิธีแก้ปัญหาด้วยวิธีที่ง่ายกว่าเช่นastและ / หรือjson.dumpsและ

ปัญหาที่ 2 - ast& json.dumps: หาก JSON เป็นทั้งที่ยกมาเดี่ยวและมีสตริงอย่างน้อยหนึ่งค่าซึ่งจะมีเครื่องหมายคำพูดเดี่ยววิธีแก้ปัญหาที่เรียบง่าย แต่ใช้งานได้จริงเพียงวิธีเดียวที่ฉันพบคือใช้ทั้งสองอย่าง:

ในตัวอย่างต่อไปนี้เราถือว่าlineเป็นวัตถุสตริง JSON ที่เข้ามา:

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

ขั้นตอนที่ 1: แปลงสตริงขาเข้าเป็นพจนานุกรมโดยใช้ast.literal_eval()
ขั้นตอนที่ 2: นำjson.dumpsไปใช้กับการแปลงคีย์และค่าที่เชื่อถือได้แต่ไม่ต้องแตะเนื้อหาของค่า :

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

json.dumpsเพียงอย่างเดียวจะไม่ทำงานเนื่องจากไม่ตีความ JSON แต่จะเห็นเฉพาะสตริง คล้ายกับast.literal_eval(): แม้ว่าจะตีความ JSON (พจนานุกรม) อย่างถูกต้อง แต่ก็ไม่ได้แปลงสิ่งที่เราต้องการ


4

คุณสามารถแก้ไขได้ด้วยวิธีนี้:

s = "{'username':'dfdsfdsf'}"
j = eval(s)

ใช้ ast.literal_eval แทนการประเมินเพื่อช่วยหลีกเลี่ยงการโจมตีด้วยการฉีดยา
Simon Kingaby

2

อย่างที่กล่าวไว้ JSON ไม่ใช่ไวยากรณ์ Python คุณต้องใช้เครื่องหมายคำพูดคู่ใน JSON ผู้สร้างมีชื่อเสียง (ใน -) ในการใช้ชุดย่อยที่เข้มงวดของไวยากรณ์ที่อนุญาตเพื่อลดการโอเวอร์โหลดทางปัญญาของโปรแกรมเมอร์


ด้านล่างนี้อาจล้มเหลวหากสตริง JSON ตัวใดตัวหนึ่งมีเครื่องหมายคำพูดเดียวตามที่ @Jiaaro ชี้ไว้ ไม่ได้ใช้. ทิ้งไว้ที่นี่เป็นตัวอย่างของสิ่งที่ไม่ได้ผล

มันเป็นประโยชน์จริงๆจะรู้ว่าไม่มีคำพูดเดียวในสตริง JSON สมมติว่าคุณคัดลอกและวางจากคอนโซลเบราว์เซอร์ / อะไรก็ได้ จากนั้นคุณสามารถพิมพ์

a = json.loads('very_long_json_string_pasted_here')

ซึ่งอาจแตกได้หากใช้เครื่องหมายคำพูดเดี่ยวด้วย


2
ไม่เป็นความจริงที่ไม่มีเครื่องหมายคำพูดเดียวในสตริง json นั่นอาจเป็นจริงในบางกรณี แต่คุณไม่สามารถวางใจได้ เช่นนี่คือ json ที่ถูกต้อง:{"key": "value 'with' single quotes"}
Jiaaro

2

มันแก้ปัญหาของฉันได้อย่างแท้จริงโดยใช้ฟังก์ชัน 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)

นี่เป็นตัวอย่างที่แย่มาก จะเกิดอะไรขึ้นถ้ามีคนพบว่าคุณใช้ eval บน json และส่งโค้ดที่มี json ผิดรูปแบบซึ่งจะถูกประเมินโดย eval?
Metonymy

1

เมื่อไม่นานมานี้ฉันได้พบกับปัญหาที่คล้ายกันมากและเชื่อว่าวิธีแก้ปัญหาของฉันจะได้ผลสำหรับคุณเช่นกัน ฉันมีไฟล์ข้อความที่มีรายการในรูปแบบ:

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

ฉันต้องการแยกวิเคราะห์ข้อมูลข้างต้นลงในรายการ python แต่ไม่สนใจ eval () เนื่องจากฉันไม่สามารถเชื่อถือข้อมูลที่ป้อนได้ ฉันลองใช้ JSON ครั้งแรก แต่ยอมรับเฉพาะรายการที่ยกมาสองครั้งดังนั้นฉันจึงเขียนคำศัพท์ง่ายๆของตัวเองสำหรับกรณีเฉพาะนี้ (เพียงเสียบ "stringtoparse" ของคุณเองและคุณจะได้รับเป็นรายการผลลัพธ์: '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

หวังว่าจะเป็นประโยชน์กับใครบางคน สนุก!


สิ่งนี้จะช่วยให้คุณไม่ได้รับจากdocs.python.org/2/library/ast.html#ast.literal_eval ?
Charles Duffy


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

ข้อมูลโค้ดด้านบนควรใช้งานได้


2
มันอาจมีประโยชน์ แต่มันไม่ตอบโจทย์ที่ถาม ปัญหาเริ่มต้นด้วยสตริงไม่ใช่รายการ
Rachel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.