15

I have a string :

'{tomatoes : 5 , livestock :{cow : 5 , sheep :2 }}' 

and would like to convert it to

{
  "tomatoes" : "5" , 
  "livestock" :"{"cow" : "5" , "sheep" :"2" }"
}

Any ideas ?

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
mwaks
  • 379
  • 2
  • 4
  • 10
  • Is there a reason you want the numbers to be captured as strings? – kindall Dec 05 '17 at 22:39
  • Not necessarily , I figured it might be easier that way. – mwaks Dec 05 '17 at 22:41
  • That's kind of paradox. You can't really change a string to a dictionary type... It's not like you can change the type to a `dict`. That's pretty interesting, I'd like to find out how this plays out... JSON is a usefull way to do it, but how about a brute force hard code way? – drewteriyaki Dec 06 '17 at 00:09
  • That's a weirdly formatted dictionary. Where did it come from? – kindall Dec 06 '17 at 04:54
  • This came from an Mac app. I need to convert it to a real python dict for a faster access. – mwaks Dec 06 '17 at 13:42

4 Answers4

22

This has been settled in 988251 In short; use the python ast library's literal_eval() function.

import ast
my_string = "{'key':'val','key2':2}"
my_dict = ast.literal_eval(my_string)
cjor
  • 569
  • 1
  • 4
  • 10
6

The problem with your input string is that it's actually not a valid JSON because your keys are not declared as strings, otherwise you could just use the json module to load it and be done with it.

A simple and dirty way to get what you want is to first turn it into a valid JSON by adding quotation marks around everything that's not a whitespace or a syntax character:

source = '{tomatoes : 5 , livestock :{cow : 5 , sheep :2 }}'

output = ""
quoting = False
for char in source:
    if char.isalnum():
        if not quoting:
            output += '"'
            quoting = True
    elif quoting:
        output += '"'
        quoting = False
    output += char

print(output)  #  {"tomatoes" : "5" , "livestock" :{"cow" : "5" , "sheep" :"2" }}

This gives you a valid JSON so now you can easily parse it to a Python dict using the json module:

import json

parsed = json.loads(output)
# {'livestock': {'sheep': '2', 'cow': '5'}, 'tomatoes': '5'}
zwer
  • 24,943
  • 3
  • 48
  • 66
  • Why json? Why not just `parsed = eval(output)`? – innisfree Dec 06 '17 at 05:34
  • 1
    @innisfree - Because no sane person should ever use `eval`. And even the insane should think twice before using it... and then use `ast.literal_eval()` instead if they really, really cannot find any way around it. Besides, it's **much** slower than `json.loads()` anyway. – zwer Dec 06 '17 at 05:46
  • OK. Why would no sane person use it? – innisfree Dec 06 '17 at 05:47
  • 3
    @innisfree - start with [Eval really is dangerous](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html), then do a search with words `eval` and `dangerous`, `bad practice`, `slow` and such right here on SO and you'll get more than a mouthful of reasons why should you not use it. In short - in 99.999% of possible use cases, there is a faster, better and safer solution without exposing your system to the dangers of running arbitrary code using `eval()`. – zwer Dec 06 '17 at 05:51
  • 1
    You're not exposing the system to *arbitrary code*, but a very particular piece of code, viz `'{tomatoes: 5, livestock: {cow: 5, sheep: 2}}'`. – innisfree Dec 06 '17 at 05:52
  • 2
    @innisfree - that's just an example, the OP already converted that string into whatever he wanted by typing out the question. It's safe to assume that it will be used on arbitrary strings outside of the OP's control (otherwise he/she could impose the format most suitable for them) and you don't want `eval()` ever touching arbitrary strings (which is why most people would want to use `eval()` anyway). – zwer Dec 06 '17 at 05:59
0

What u have is a JSON formatted string which u want to convert to python dictionary.

Using the JSON library :

import json
with open("your file", "r") as f:
    dictionary =  json.loads(f.read());

Now dictionary contains the data structure which ur looking for.

Community
  • 1
  • 1
Natesh bhat
  • 12,274
  • 10
  • 84
  • 125
-2

Here is my answer:

dict_str = '{tomatoes: 5, livestock: {cow: 5, sheep: 2}}'

def dict_from_str(dict_str):    

    while True:

        try:
            dict_ = eval(dict_str)
        except NameError as e:
            key = e.message.split("'")[1]
            dict_str = dict_str.replace(key, "'{}'".format(key))
        else:
            return dict_


print dict_from_str(dict_str)

My strategy is to convert the dictionary str to a dict by eval. However, I first have to deal with the fact that your dictionary keys are not enclosed in quotes. I do that by evaluating it anyway and catching the error. From the error message, I extract the key that was interpreted as an unknown variable, and enclose it with quotes.

innisfree
  • 2,044
  • 1
  • 14
  • 24
  • I'm not sure whether hacking the error message is stupid or smart :D – innisfree Dec 06 '17 at 05:45
  • 1
    People tend to be touchy about using `eval`, bear in mind there is a function in the `ast` module called [`literal_eval`](https://docs.python.org/3/library/ast.html#ast.literal_eval), which has a much reduced scope for evaluation and hence is more secure. – Paul Rooney Apr 05 '18 at 00:21
  • @paul haha yes, I noticed that ;) – innisfree Apr 05 '18 at 00:24