6

I have a string which basically contains a bunch of JSON formatted text that I'd ultimately like to export to Excel in "pretty print" format with the proper indentations for nesting, etc.

It's imperative that the original order of key/values is retained for readability purposes. My thought process to accomplish what I want is to

a) use something like eval to convert the string to a dictionary and b) use OrderedDict from the collections library to keep the order intact.

However I'm not getting the expected result:

In [21]: json_string = str({"id":"0","last_modified":"undefined"})
In [22]: OrderedDict(eval(json_string))
Out[23]: OrderedDict([('last_modified', 'undefined'), ('id', '0')])

I also haven't quite figured out yet how I'm going to write the output to excel in pretty print format, but I'd hope that'd be the comparatively easy part!

ChrisArmstrong
  • 2,491
  • 8
  • 37
  • 60

3 Answers3

20

You can use the object_pairs_hook argument to JSONDecoder to change the decoded dictionaries to OrderedDict:

import collections
import json

decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict)

json_string = '{"id":"0","last_modified":"undefined"}'
print decoder.decode(json_string)
json_string = '{"last_modified":"undefined","id":"0"}'
print decoder.decode(json_string)

This prints:

OrderedDict([(u'id', u'0'), (u'last_modified', u'undefined')])
OrderedDict([(u'last_modified', u'undefined'), (u'id', u'0')])
jterrace
  • 64,866
  • 22
  • 157
  • 202
7

First, you should consider using json (or even ast.literal_eval) instead of eval.

Secondly, this won't work because the minute you turn it into a regular dictionary, all order is lost. You'll need to parse the "json" yourself if you want to put the information into an OrderedDict.

Fortunately, this isn't quite as hard as you might think if you use the ast module. Here I'm assuming that the dictionary only contains strings but it shouldn't be too hard to modify for other purposes.

s = '{"id":"0","last_modified":"undefined"}'
import ast
from collections import OrderedDict
class DictParser(ast.NodeVisitor):
    def visit_Dict(self,node):
        keys,values = node.keys,node.values
        keys = [n.s for n in node.keys]
        values = [n.s for n in node.values]
        self.od = OrderedDict(zip(keys,values))

dp = DictParser()
dp.visit(ast.parse(s))
ordered_dict = dp.od
print ordered_dict
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • hmm, I thought this might be the case. Perhaps it'd be best to use regular expressions reformat the JSON string (ie replace commas with line breaks, etc) – ChrisArmstrong May 20 '13 at 00:59
  • I do like ast. I just figured out how to use it a few months back and so it's what I think about first when it comes to parsing python-like strings. – mgilson May 20 '13 at 01:15
  • @mgilson while I went with the above answer, this is indeed cool--thanks for contributing :) – ChrisArmstrong May 20 '13 at 01:25
  • @ChrisArmstrong -- I'm very glad you chose the other answer as that is the way this *should* be done. If you had accepted mine, I would be forced to delete it :). I suppose I'm happy enough to leave it as an example of some of the neat things you can do with `ast` as long as it's clear the other answer is better for solving this particular problem. – mgilson May 20 '13 at 01:28
0

This post is related to string to ordereddict conversion with string manipulation:

https://stackoverflow.com/a/27177986/1128709

Community
  • 1
  • 1
Guray Celik
  • 1,281
  • 1
  • 14
  • 13