188

My code:

import simplejson as json

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

#1 definition is wrong

#2 definition is right

I heard that in Python that single and double quote can be interchangable. Can anyone explain this to me?

themefield
  • 3,847
  • 30
  • 32
Bin Chen
  • 61,507
  • 53
  • 142
  • 183

11 Answers11

238

JSON syntax is not Python syntax. JSON requires double quotes for its strings.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 2
    but first one it's single quote in JSON, I am confused. That one can pass compile but the second one can't. – Bin Chen Nov 12 '10 at 08:04
  • 10
    Thank you for this confirmation. Apparently I'm the only one importing `str(dict)`, and doesn't want to `eval` it. A simple `.replace("'", '"')` should do the trick. – isaaclw Dec 13 '12 at 22:16
  • 12
    And I spoke too soon. Apparently it's more complicated than that. – isaaclw Dec 13 '12 at 22:45
  • 7
    If you need to use double quotes all around, you can call `json.dumps(..)` twice as in: `import json; d = dict(tags=["dog", "cat", "mouse"]); print json.dumps(json.dumps(d))` which gives: `"{\"tags\": [\"dog\", \"cat\", \"mouse\"]}"` – rprasad Oct 18 '16 at 18:33
  • 1
    Trying to clarify a little bit: Both `"{'username':'dfdsfdsf'}"` and `'{"username":"dfdsfdsf"}'` are valid ways to make a string in Python. But the first one contains `'` symbols, and the second one contains `"` symbols. The string contents must use `"` symbols in order for it to be a valid JSON string that can be used with the `json` standard library. You can use `"` to surround a string that contains `"`, if you escape them, like: `"{\"username\":\"dfdsfdsf\"}"`. – Karl Knechtel Jul 02 '22 at 03:22
193

you can use ast.literal_eval()

>>> import ast
>>> s = "{'username':'dfdsfdsf'}"
>>> ast.literal_eval(s)
{'username': 'dfdsfdsf'}
hahakubile
  • 6,978
  • 4
  • 28
  • 18
  • 26
    I like this answer the best: you don't often have a choice: if someone gives you single quotes, you got single quotes. Either json.loads needs an extra argument, or you should use this. Globally replacing "'" is a disaster, as what if the incoming data is: `{ 'a' : 'this "string" really isn\'t!!!!' }` – Mark Gerolimatos Dec 28 '15 at 22:21
  • 1
    @Mark, can this method be adapted to a trickier situation with nested quotes eg `"{'link':'http://my.com'}"` ? In this case, `ast.literal_eval` throws syntax error – alancalvitti Apr 23 '19 at 16:04
  • 3
    This seems like a security risk to me. – JacksonHaenchen Nov 26 '19 at 20:12
  • 4
    ``ast.literal_eval`` is not a security risk, its ``eval`` that would be. – bones225 Oct 17 '20 at 02:50
  • I'd rather use `repr`, since `literal_eval` is unnecesarily powerful for this. – user7610 Nov 06 '21 at 20:32
  • @user7610 `repr` solves the opposite problem, of creating a textual representation of the data structure. `literal_eval` is somewhat powerful, but it's much more restricted than `eval`, and there's really nothing better for parsing this kind of data. That said: none of this really answers the question, it just offers a workaround without any real explanation. – Karl Knechtel Jul 02 '22 at 03:24
  • This doesn't handle `null`. – gre_gor Aug 22 '23 at 07:17
68

You can dump JSON with double quote by:

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
  • 1,013
  • 7
  • 4
  • 30
    this goes the wrong way. you are serializing python data structures to JSON; the original question is about deserializing JSON to python data structures. – tedder42 Feb 26 '15 at 20:20
  • 8
    The idea would be to serialize the python into json with json.dumps, then call json.loads on it when it is in the str form. – jheld Apr 09 '16 at 21:10
  • 5
    You miss understand here. If you want to load json string, it has to be double quote. What you are doing is still dump json, not json string. – LegitMe Apr 20 '16 at 06:22
  • 1
    Thanks, this worked for me! `json.loads(json.dumps(single_quotes_json))` – Mark Jan 27 '21 at 16:42
  • 2
    Not sure about the original question, but this definitely helped me translate JSON data properly with the double-quotes before inserting it into SQL Server to allow for valid JSON parsing. Thanks! – Ryan Harris Mar 08 '21 at 09:37
  • 1
    @Mark if that worked for you, then you could have just used `single_quotes_json` directly. If `single_quotes_json` had actually been a string with single quotes in it, then this round-trip would just give you the string back. If you got back a dict, it's because you already had one. – Karl Knechtel Jul 02 '22 at 03:26
23

Two issues with answers given so far, if , for instance, one streams such non-standard JSON. Because then one might have to interpret an incoming string (not a python dictionary).

Issue 1 - demjson: With Python 3.7.+ and using conda I wasn't able to install demjson since obviosly it does not support Python >3.5 currently. So I need a solution with simpler means, for instance astand/or json.dumps.

Issue 2 - ast & json.dumps: If a JSON is both single quoted and contains a string in at least one value, which in turn contains single quotes, the only simple yet practical solution I have found is applying both:

In the following example we assume line is the incoming JSON string object :

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

Step 1: convert the incoming string into a dictionary using ast.literal_eval()
Step 2: apply json.dumps to it for the reliable conversion of keys and values, but without touching the contents of values:

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

json.dumps alone would not do the job because it does not interpret the JSON, but only see the string. Similar for ast.literal_eval(): although it interprets correctly the JSON (dictionary), it does not convert what we need.

Siegfried Heide
  • 352
  • 2
  • 7
19

demjson is also a good package to solve the problem of bad json syntax:

pip install demjson

Usage:

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

Edit:

demjson.decode is a great tool for damaged json, but when you are dealing with big amourt of json data ast.literal_eval is a better match and much faster.

Community
  • 1
  • 1
Dhia
  • 10,119
  • 11
  • 58
  • 69
  • 5
    `demjson.decode` is a great tool for damaged json -- but for tasks involving tens or hundreds of thousands of json packets, `ast.literal_eval` is much faster. Not to say `demjson` doesn't have it's place: I use it as fallback in case faster methods fail. – mjwunderlich Jan 20 '16 at 18:53
  • 1
    Actually demjson is one worked much better, instead of testing against ast.literal_eval and json.loads – Marware Dec 14 '19 at 19:25
7

You can fix it that way:

s = "{'username':'dfdsfdsf'}"
j = eval(s)
robinch
  • 188
  • 2
  • 5
2

As said, JSON is not Python syntax. You need to use double quotes in JSON. Its creator is (in-)famous for using strict subsets of allowable syntax to ease programmer cognitive overload.


Below can fail if one of the JSON strings itself contains a single quote as pointed out by @Jiaaro. DO NOT USE. Left here as an example of what does not work.

It is really useful to know that there are no single quotes in a JSON string. Say, you copied and pasted it from a browser console/whatever. Then, you can just type

a = json.loads('very_long_json_string_pasted_here')

This might otherwise break if it used single quotes, too.

serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • 3
    it's not true that there are no single quotes in a json string. That may be true in a specific case, but you can't rely on it. e.g., this is valid json: `{"key": "value 'with' single quotes"}` – Jiaaro Apr 10 '19 at 20:53
1

It truly solved my problem using eval function.

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
  • 344
  • 3
  • 8
  • 2
    This is a very bad example. What if someone finds out you're using eval on json and sends a malformed json containing code which then is evaluated by eval? – Denis Nutiu Apr 02 '20 at 12:24
  • 2
    **[Please do not use this](https://stackoverflow.com/questions/1832940/why-is-using-eval-a-bad-practice). It is a security risk.** – Karl Knechtel Jul 02 '22 at 03:57
0

I recently came up against a very similar problem, and believe my solution would work for you too. I had a text file which contained a list of items in the form:

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

I wanted to parse the above into a python list but was not keen on eval() as I couldn't trust the input. I tried first using JSON but it only accepts double quoted items, so I wrote my own very simple lexer for this specific case (just plug in your own "stringtoparse" and you will get as output list: '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

Hopefully it is useful to somebody. Enjoy!

Matt
  • 39
  • 5
0

You can use

json.dumps(your_json, separators=(",", ":"))
-2
import json
#1
single_quote_dictioary = "{'username':'dfdsfdsf'}"
json.loads(single_quote_dictioary)

the out put error: json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

that error is not from python side, it is how JSON format works!


Mahdi Hamzeh
  • 289
  • 2
  • 8