Question

I know that this question sound a duplicate, but it's not, at least looked for a while and I couldn't fine nothing for my specific problem.

I have the following string:

"{first : {name : 'test', value : 100}, second : {name : 'test2', value : 50}}"

And I want to convert that string to a dictionary something like this:

{'first': {'name': 'test', 'value' : 100}, 'second': {'name': 'test2', 'value' : 50}}

Any ideas? I'm working with Python 2.5

Thanks

Was it helpful?

Solution

First, if you're generating these strings from dictionaries (in Python or almost any other language), you might want to consider generating them in a way that can be easily parse. For example, in Python. either repr(d) or json.dumps(d) will give you something very similar to your existing string, but with proper quotes.


But if you've just got a mess of strings someone else gave you, the easiest thing to do might be to regex it into an actual JSON string so you can parse it:

json.loads(re.sub(r",\s*(\w+)", r", '\1'", 
                  re.sub(r"\s*\{\s*(\w+)", r"{'\1'", x)).replace("'", '"'))

In 2.5, there's no built-in json module, so you probably want to pip install simplejson and then you can do this:

try:
  import json
except ImportError:
  import simplejson as json

(Or, of course, you can just require simplejson unconditionally, if you prefer.)


A different alternative is to use ast.literal_eval instead of json.loads. Which one is appropriate depends on more information about your strings. JSON is more restricted than Python syntax, so it's safer if you're worried about the source of your input, but not as flexible if your input might legitimately have stuff that JSON can't handle, like 1+3j.

However, like json, ast is new to 2.6, and there's no drop-in replacement available on PyPI. Fortunately, 2.5 does have the _ast module, and you can just copy and paste the source literal_eval from 2.6's ast.py, but this is a bit of a hassle. There are also recipes at ActiveState, such as http://code.activestate.com/recipes/364469/ that, while not identical to literal_eval, may be right for your purposes.

This still won't quite work, because you're also missing the closing right brace in the example string. I'm hoping that's a typo, in which case there's not a problem.

If not, you need to explain what you want it to actually do with such cases. Maybe auto-close any unclosed braces? If so, could unclosed brackets also be a problem?

OTHER TIPS

You can try using pyparsing, which is backward-compatible to Python 2.5 - see comments in annotated code below:

from pyparsing import Suppress, Forward, Word, nums, quotedString, removeQuotes, alphas, alphanums, Group, delimitedList

# define some punctuation expressions
LBRACE,RBRACE,COLON = map(Suppress,"{}:")

# forward declare dict_, because we will use it as part of defining
# dict_value, which we will then use to define dict_ (i.e., the grammar
# is recursive)
dict_ = Forward()

# what does a key value look like (guessing it is any word that
# starts with an alpha or '_', followed by zero or more alphas, nums, or '_'s)
dict_key = Word(alphas+'_',alphanums+'_')

# define possible values for dict entries (expand as needed)
# parse actions do data conversion during parsing, so that we get native Python types,
# not just strings for everything that we have to convert later
integer = Word(nums).setParseAction(lambda t:int(t[0]))
quotedString.setParseAction(removeQuotes)
dict_value = quotedString | integer | dict_

# a dict element is key : value
dict_element = Group(dict_key + COLON + dict_value)

# use a parse action to convert parsed data to dicts while parsing
make_dict = lambda t: dict(t.asList())

# finally, define dict_ using '<<' operator to "inject" the pattern into the previously
# defined Forward - delimitedList(expr) is a short-cut for expr + ZeroOrMore(',' + expr)
dict_ << (LBRACE + (delimitedList(dict_element).setParseAction(make_dict) + RBRACE))

# parse the input string - we get back a real dict, not just a hierarchical list of strings
data = "{first : {name : 'test', value : 100}, second : {name : 'test2', value : 50}}"
dd = dict_.parseString(data)[0]
print type(dd)
print dd
print dd.keys()
print dd['first'].keys()

prints:

<type 'dict'>
{'second': {'name': 'test2', 'value': 50}, 'first': {'name': 'test', 'value': 100}}
['second', 'first']
['name', 'value']
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top