Котирування синглів проти подвійних в JSON


107

Мій код:

import simplejson as json

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

#1 визначення неправильне

#2 визначення правильне

Я чув, що в Python ця одиночна та подвійна цитата може бути взаємозамінною. Хтось може мені це пояснити?

Відповіді:


169

Синтаксис JSON не є синтаксисом Python. JSON вимагає подвійних лапок для своїх рядків.


2
але спочатку це єдина цитата в JSON, я розгублений. Цей може передати компіляцію, але другий не може.
Бін Чен

6
Дякую за це підтвердження. Мабуть, я єдиний імпортер 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

124

ви можете використовувати ast.literal_eval()

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

9
Мені найкраще подобається ця відповідь: у вас часто немає вибору: якщо хтось дає вам одинарні цитати, у вас є одиничні цитати. Або json.loads потребує додаткового аргументу, або ви повинні використовувати його. Глобальна заміна "" "- це катастрофа, як, що робити, якщо вхідні дані:{ 'a' : 'this "string" really isn\'t!!!!' }
Марк Героліматос

@ Марк, чи можна адаптувати цей метод до складнішої ситуації із вкладеними цитатами, наприклад "{'link':'<a href="mylink">http://my.com</a>'}"? У цьому випадку ast.literal_eval
викидається

1
Мені це здається ризиком для безпеки.
JacksonHaenchen

2
Як це відповідає на питання? Що це має відношення до одинарних проти подвійних лапок JSON? Такий підхід ast може дозволити завантажувати діктову Python із рядка, але головна проблема, яку має ОП, полягає в тому, що рядок №1 не є дійсною JSON, тоді як рядок №2 є.
jschultz410

43

Ви можете скинути 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) 

12
це йде неправильним шляхом. ви серіалізуєте структури даних python в JSON; оригінальне питання стосується десеріалізації JSON для структур даних python.
tedder42

5
Ідея полягала б у тому, щоб серіалізувати python у json за допомогою json.dumps, а потім викликати json.loads на ньому, коли він знаходиться у формі str.
jheld

3
Ви сумуєте тут зрозуміти. Якщо ви хочете завантажити рядок json, це має бути подвійна цитата. Те, що ви робите, - це все ж дамп json, а не json string.
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

4

Два питання з даними відповідями, якщо, наприклад, один потік такого нестандартного JSON. Тому що тоді, можливо, доведеться інтерпретувати вхідний рядок (а не словник python).

Випуск 1 - demjson: З Python 3.7. + Та за допомогою conda мені не вдалося встановити demjson, оскільки obviosly він не підтримує 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 (словник), він не перетворює те, що нам потрібно.


3

Ви можете це виправити таким чином:

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

використовуйте ast.literal_eval замість 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?
Метонімія

1

Нещодавно я зіткнувся з дуже подібною проблемою, і вважаю, що моє рішення спрацювало б і для вас. У мене був текстовий файл, який містив перелік елементів у формі:

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

Я хотів проаналізувати вищезазначене до списку пітонів, але не захопився 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 ?
Чарльз Даффі

0
import ast 
answer = subprocess.check_output(PYTHON_ + command, shell=True).strip()
    print(ast.literal_eval(answer.decode(UTF_)))

Працює для мене


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

Вищенаведений фрагмент коду повинен працювати.


2
Це може зробити щось корисне, але це не відповідає на запитання, яке було задано. Проблема починається з рядка, а не з списку.
Рейчел
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.