Convert python dictionary to JavaScript object literal - python

Using json.dumps I can convert a dictionary to json format like this:
>>> from json import dumps
>>> dumps({'height': 100, 'title': 'some great title'})
'{"title": "some great title", "height": 100}'
However, I'm looking to turn my dictionary into a javascript literal, like this:
{title: "some great title", height: 100}
(Notice there are no double quotes around title and height.)
Is there a Python library to do this?

If you know all your keys are valid tokens you can use this simple code:
import json
def js_list(encoder, data):
pairs = []
for v in data:
pairs.append(js_val(encoder, v))
return "[" + ", ".join(pairs) + "]"
def js_dict(encoder, data):
pairs = []
for k, v in data.iteritems():
pairs.append(k + ": " + js_val(encoder, v))
return "{" + ", ".join(pairs) + "}"
def js_val(encoder, data):
if isinstance(data, dict):
val = js_dict(encoder, data)
elif isinstance(data, list):
val = js_list(encoder, data)
else:
val = encoder.encode(data)
return val
encoder = json.JSONEncoder(ensure_ascii=False)
js_val(encoder, {'height': 100, 'title': 'some great title'})
The return value of js_val() is in your desired format.

Dirty hack, but could work: subclass JSONEncoder and override _iterencode_dict method, so that it yields key formatted as you want. In python3.3 it is in line 367 of json.encoder module in line yield _encoder(key). You probably want to copy the whole method and change this line to something like yield key if isinstance(key, str) else _encoder(key).
Again - this is really dirty, unless you have no other option - don't do that. Though, it is doable, what is worth knowing if you really need it.

Related

Massaging XML to JSON output for front-end parsing

Using the xmltodict (v0.12.0) on python, I have an xml that will get parsed and converted into a json format. For example:
XML:
<test temp="temp" temp2="temp2">This is a test</test>
Will get converted to the following json:
"test": {
"#temp": "temp",
"#temp2": "temp2",
"#text": "This is a test"
}
I have a front end parser that reads JSON objects and converts them into XML. Unfortunately, the tags are required to be shaped in a different way.
What the front end parser expects:
{
test: {
"#": {
temp: "temp",
temp2: "temp2"
},
"#": "This is a test"
}
}
I feel like this formatting is better served to be modified on Python but I am having a bit of trouble iterating a much larger dictionary, where we don't know how deep an xml would go, and collecting all of the keys that start with "#" and giving that it's own object within the overall tag object. What are some ways I could approach shaping this data?
For anyone who is curious, this is how I ended up solving the issue. Like #furas stated, I decided that recursion was my best bet. I ended up iterating through my original xml data I converted to JSON with the incorrect formatting of attributes, then creating a copy while finding any attribute markers:
def structure_xml(data):
curr_dict = {}
for key,value in data.items():
if isinstance(value, dict):
curr_dict[key] = structure_xml(value)
elif isinstance(value, list):
value_list = []
for val in value:
if isinstance(val,dict) or isinstance(val,list):
value_list.append(structure_xml(val))
curr_dict[key] = value_list
else:
if '#' in key:
new_key = key.split("#",1)[1]
new_obj = { new_key: value }
if "#" in curr_dict:
curr_dict["#"][new_key] = value
else:
curr_dict["#"] = new_obj
elif '#text' in key:
curr_dict['#'] = data[key]
else:
curr_dict[key] = data[key]
return curr_dict

How to return a json file line by line in flask?

I am writing a api to download json file. Suppose I have 3 dict:
{"task_name":"task1","task_info":"aaaaa"}
{"task_name":"task2","task_info":"bbbbb"}
{"task_name":"task3","task_info":"ccccc"}
I want to return these dict into one json file.Here is what I did:
data = json.dumps(tasks_info, default=str)
response = make_response(data, 200, {'mimetype': 'application/json'})
response.headers['Content-Disposition'] = "attachment;filename={}.json".format(urllib.parse.quote('result'))
return response
tasks_info is a list that contains 3 dict.
The result file open like that:
[{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]
It is a very long line.
What I want to get looks like
[ // the '[' and ']' is not necessary
{"task_name":"task1","task_info":"aaaaa"},
{"task_name":"task2","task_info":"bbbbb"},
{"task_name":"task3","task_info":"ccccc"}
]
I want every dict show in a distinct line instead of every dict shown in same line.
Are there any way to change the result file look like?
The relevant command that formats this output takes place in json.dumps(). There are a bunch of commands you can give to format your json. A conventional approach is to define the indent level:
import json
tasks_info = [{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]
data = json.dumps(tasks_info, indent=2)
print(data)
Which pretty-prints the json into:
[
{
"task_info": "aaaaa",
"task_name": "task1"
},
{
"task_info": "bbbbb",
"task_name": "task2"
},
{
"task_info": "ccccc",
"task_name": "task3"
}
]
To achieve the exact formatting you desire, you need to define your own formatter class. I used this answer and edited it to fit your formatting requirements. Here is the full code:
import _ctypes
import json
import re
class OneDictPerLine(object):
def __init__(self, value):
self.value = value
def __repr__(self):
if not isinstance(self.value, list):
return repr(self.value)
else: # Sort the representation of any dicts in the list.
reps = ('{{{}}}'.format(', '.join(
('{!r}: {!r}'.format(k, v) for k, v in sorted(v.items()))
)) if isinstance(v, dict)
else
repr(v) for v in self.value)
return '[ \n' + ',\n'.join(reps) + '\n ]'
def di(obj_id):
""" Reverse of id() function. """
# from https://stackoverflow.com/a/15012814/355230
return _ctypes.PyObj_FromPtr(obj_id)
class MyEncoder(json.JSONEncoder):
FORMAT_SPEC = "##{}##"
regex = re.compile(FORMAT_SPEC.format(r"(\d+)"))
def default(self, obj):
return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, OneDictPerLine)
else super(MyEncoder, self).default(obj))
def encode(self, obj):
format_spec = self.FORMAT_SPEC # Local var to expedite access.
json_repr = super(MyEncoder, self).encode(obj) # Default JSON repr.
# Replace any marked-up object ids in the JSON repr with the value
# returned from the repr() of the corresponding Python object.
for match in self.regex.finditer(json_repr):
id = int(match.group(1))
# Replace marked-up id with actual Python object repr().
json_repr = json_repr.replace(
'"{}"'.format(format_spec.format(id)), repr(di(id)))
return json_repr
tasks_info = [{"task_name":"task1","task_info":"aaaaa"},{"task_name":"task2","task_info":"bbbbb"},{"task_name":"task3","task_info":"ccccc"}]
data = json.dumps(OneDictPerLine(tasks_info), cls=MyEncoder)
print(data)
#make response and return data ...
output:
[
{'task_info': 'aaaaa', 'task_name': 'task1'},
{'task_info': 'bbbbb', 'task_name': 'task2'},
{'task_info': 'ccccc', 'task_name': 'task3'}
]

How to change a text in JSON that has quotes in it?

The following code works on python without any issue. It opens the JSON file, and replace all bananas to apples:
import json
replacements = "banana" : "apple"
with open(mycodepath, 'r') as file:
data = file.read()
for old, new in replacements.items():
data = data.replace(old, new)
However, I want to replace "ArmReoriented", "visible": true with "ArmReoriented", "visible": false,
I tried using triple quotes, but it does not work.
replacements = """ArmReoriented", "visible": true""" : """ArmReoriented", "visible": false,"""
How I replace a text containing quotes on JSON using Python?
Using """ is basically a comment or a doc-string and would not work.
Put double quote between a single quote string like below.
replacements = {'"ArmReoriented", "visible": true' : '"ArmReoriented", "visible": false'}
Try switching to single quotes in your replacements dict:
replacements = {'"ArmReoriented", "visible": true': '"ArmReoriented", "visible": false,'}
data = 'asd asd asd "ArmReoriented", "visible": true asd asd'
for old, new in replacements.items():
data = data.replace(old, new)
print(data)
You also may escape double quotes both from key and from value in your replacement dict:
replacements = {"\"ArmReoriented\", \"visible\": true": "\"ArmReoriented\", \"visible\": false,"}
If we know what key the value ArmReoriented is attached to, we can do this the right way, processing your content structured data rather than as a string:
import json, copy
def hideReorientedArm(obj):
if isinstance(obj, dict):
if obj.get("LastEvent") == "ArmReoriented" and obj.get("visible") is True:
obj["visible"] = False
return obj
def walk(obj, updateFn):
if isinstance(obj, list):
obj = [walk(elem, updateFn) for elem in obj]
elif isinstance(obj, dict):
obj = {k: walk(v, updateFn) for k, v in obj.items()}
return updateFn(obj)
with open(mycodepath, 'r') as file:
data = json.load(file)
data = walk(data, hideReorientedArm)
See this running at https://ideone.com/Zr6546
Even if you don't know the name of the specific key having that value, you can still search all of them if that's necessary for some reason, replacing the shorter hideReorientedArm definition above with something more like the following:
def hideReorientedArm(obj):
if isinstance(obj, dict):
if obj.get("visible") is True:
foundArmReoriented = False
for (k,v) in obj.items():
if v == "ArmReoriented":
foundArmReoriented = True
break
if foundArmReoriented:
obj["visible"] = False
return obj
...see this version running at https://ideone.com/z34Mx1

How to limit the number of float digits JSONEncoder produces?

I am trying to set the python json library up in order to save to file a dictionary having as elements other dictionaries. There are many float numbers and I would like to limit the number of digits to, for example, 7.
According to other posts on SO encoder.FLOAT_REPR shall be used. However it is not working.
For example the code below, run in Python3.7.1, prints all the digits:
import json
json.encoder.FLOAT_REPR = lambda o: format(o, '.7f' )
d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
f = open('test.json', 'w')
json.dump(d, f, indent=4)
f.close()
How can I solve that?
It might be irrelevant but I am on macOS.
EDIT
This question was marked as duplicated. However in the accepted answer (and until now the only one) to the original post it is clearly stated:
Note: This solution doesn't work on python 3.6+
So that solution is not the proper one. Plus it is using the library simplejson not the library json.
It is still possible to monkey-patch json in Python 3, but instead of FLOAT_REPR, you need to modify float. Make sure to disable c_make_encoder just like in Python 2.
import json
class RoundingFloat(float):
__repr__ = staticmethod(lambda x: format(x, '.2f'))
json.encoder.c_make_encoder = None
if hasattr(json.encoder, 'FLOAT_REPR'):
# Python 2
json.encoder.FLOAT_REPR = RoundingFloat.__repr__
else:
# Python 3
json.encoder.float = RoundingFloat
print(json.dumps({'number': 1.0 / 81}))
Upsides: simplicity, can do other formatting (e.g. scientific notation, strip trailing zeroes etc). Downside: it looks more dangerous than it is.
Option 1: Use regular expression matching to round.
You can dump your object to a string using json.dumps and then use the technique shown on this post to find and round your floating point numbers.
To test it out, I added some more complicated nested structures on top of the example you provided::
d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
d["mylist"] = [1.23456789, 12, 1.23, {"foo": "a", "bar": 9.87654321}]
d["mydict"] = {"bar": "b", "foo": 1.92837465}
# dump the object to a string
d_string = json.dumps(d, indent=4)
# find numbers with 8 or more digits after the decimal point
pat = re.compile(r"\d+\.\d{8,}")
def mround(match):
return "{:.7f}".format(float(match.group()))
# write the modified string to a file
with open('test.json', 'w') as f:
f.write(re.sub(pat, mround, d_string))
The output test.json looks like:
{
"val": 5.7868688,
"name": "kjbkjbkj",
"mylist": [
1.2345679,
12,
1.23,
{
"foo": "a",
"bar": 9.8765432
}
],
"mydict": {
"bar": "b",
"foo": 1.9283747
}
}
One limitation of this method is that it will also match numbers that are within double quotes (floats represented as strings). You could come up with a more restrictive regex to handle this, depending on your needs.
Option 2: subclass json.JSONEncoder
Here is something that will work on your example and handle most of the edge cases you will encounter:
import json
class MyCustomEncoder(json.JSONEncoder):
def iterencode(self, obj):
if isinstance(obj, float):
yield format(obj, '.7f')
elif isinstance(obj, dict):
last_index = len(obj) - 1
yield '{'
i = 0
for key, value in obj.items():
yield '"' + key + '": '
for chunk in MyCustomEncoder.iterencode(self, value):
yield chunk
if i != last_index:
yield ", "
i+=1
yield '}'
elif isinstance(obj, list):
last_index = len(obj) - 1
yield "["
for i, o in enumerate(obj):
for chunk in MyCustomEncoder.iterencode(self, o):
yield chunk
if i != last_index:
yield ", "
yield "]"
else:
for chunk in json.JSONEncoder.iterencode(self, obj):
yield chunk
Now write the file using the custom encoder.
with open('test.json', 'w') as f:
json.dump(d, f, cls = MyCustomEncoder)
The output file test.json:
{"val": 5.7868688, "name": "kjbkjbkj", "mylist": [1.2345679, 12, 1.2300000, {"foo": "a", "bar": 9.8765432}], "mydict": {"bar": "b", "foo": 1.9283747}}
In order to get other keyword arguments like indent to work, the easiest way would be to read in the file that was just written and write it back out using the default encoder:
# write d using custom encoder
with open('test.json', 'w') as f:
json.dump(d, f, cls = MyCustomEncoder)
# load output into new_d
with open('test.json', 'r') as f:
new_d = json.load(f)
# write new_d out using default encoder
with open('test.json', 'w') as f:
json.dump(new_d, f, indent=4)
Now the output file is the same as shown in option 1.
Here's something that you may be able to use that's based on my answer to the question:
Write two-dimensional list to JSON file.
I say may because it requires "wrapping" all the float values in the Python dictionary (or list) before JSON encoding it with dump().
(Tested with Python 3.7.2.)
from _ctypes import PyObj_FromPtr
import json
import re
class FloatWrapper(object):
""" Float value wrapper. """
def __init__(self, value):
self.value = value
class MyEncoder(json.JSONEncoder):
FORMAT_SPEC = '##{}##'
regex = re.compile(FORMAT_SPEC.format(r'(\d+)')) # regex: r'##(\d+)##'
def default(self, obj):
return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, FloatWrapper)
else super(MyEncoder, self).default(obj))
def iterencode(self, obj, **kwargs):
for encoded in super(MyEncoder, self).iterencode(obj, **kwargs):
# Check for marked-up float values (FloatWrapper instances).
match = self.regex.search(encoded)
if match: # Get FloatWrapper instance.
id = int(match.group(1))
float_wrapper = PyObj_FromPtr(id)
json_obj_repr = '%.7f' % float_wrapper.value # Create alt repr.
encoded = encoded.replace(
'"{}"'.format(self.FORMAT_SPEC.format(id)), json_obj_repr)
yield encoded
d = dict()
d['val'] = FloatWrapper(5.78686876876089075543) # Must wrap float values.
d['name'] = 'kjbkjbkj'
with open('float_test.json', 'w') as file:
json.dump(d, file, cls=MyEncoder, indent=4)
Contents of file created:
{
"val": 5.7868688,
"name": "kjbkjbkj"
}
Update:
As I mentioned, the above requires all the float values to be wrapped before calling json.dump(). Fortunately doing that could be automated by adding and using the following (minimally tested) utility:
def wrap_type(obj, kind, wrapper):
""" Recursively wrap instances of type kind in dictionary and list
objects.
"""
if isinstance(obj, dict):
new_dict = {}
for key, value in obj.items():
if not isinstance(value, (dict, list)):
new_dict[key] = wrapper(value) if isinstance(value, kind) else value
else:
new_dict[key] = wrap_type(value, kind, wrapper)
return new_dict
elif isinstance(obj, list):
new_list = []
for value in obj:
if not isinstance(value, (dict, list)):
new_list.append(wrapper(value) if isinstance(value, kind) else value)
else:
new_list.append(wrap_type(value, kind, wrapper))
return new_list
else:
return obj
d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
with open('float_test.json', 'w') as file:
json.dump(wrap_type(d, float, FloatWrapper), file, cls=MyEncoder, indent=4)
Here is a python code snippet that shows how to quantize json output to the specified number of digits:
#python example code, error handling not shown
#open files
fin = open(input_file_name)
fout = open(output_file_name, "w+")
#read file input (note this could be done in one step but breaking it up allows more flexibilty )
indata = fin.read()
# example quantization function
def quant(n):
return round((float(n) * (10 ** args.prec))) / (
10 ** args.prec
) # could use decimal.quantize
# process the data streams by parsing and using call back to quantize each float as it parsed
outdata = json.dumps(json.loads(indata, parse_float=quant), separators=(",", ":"))
#write output
fout.write(outdata)
The above is what the jsonvice command-line tool uses to quantize the floating-point json numbers to whatever precision is desired to save space.
https://pypi.org/project/jsonvice/
This can be installed with pip or pipx (see docs).
pip3 install jsonvice
Disclaimer: I wrote this when needing to test quantized machine learning model weights.
I found the above options within the python standard library to be very limiting and cumbersome, so if you're not strictly limited to the python standard lib, pandas has a json module that includes a dumps method which has a double_precision parameter to control the number of digits in a float (default 10):
import json
import pandas.io.json
d = {
'val': 5.78686876876089075543,
'name': 'kjbkjbkj',
}
print(json.dumps(d))
print(pandas.io.json.dumps(d))
print(pandas.io.json.dumps(d, double_precision=5))
gives:
{"val": 5.786868768760891, "name": "kjbkjbkj"}
{"val":5.7868687688,"name":"kjbkjbkj"}
{"val":5.78687,"name":"kjbkjbkj"}
Doesn't answer this question, but for the decoding side, you could do something like this, or override the hook method.
To solve this problem with this method though would require encoding, decoding, then encoding again, which is overly convoluted and no longer the best choice. I assumed Encode had all the bells and whistles Decode did, my mistake.
# d = dict()
class Round7FloatEncoder(json.JSONEncoder):
def iterencode(self, obj):
if isinstance(obj, float):
yield format(obj, '.7f')
with open('test.json', 'w') as f:
json.dump(d, f, cls=Round7FloatEncoder)
Inspired by this answer, here is a solution that works for Python >= 3.6 (tested with 3.9) and that allows customization of the format on a case by case basis. It works for both json and simplejson (tested with json=2.0.9 and simplejson=3.17.6).
Note however that this is not thread-safe.
from contextlib import contextmanager
class FormattedFloat(float):
def __new__(self, value, fmt=None):
return float.__new__(self, value)
def __init__(self, value, fmt=None):
float.__init__(value)
if fmt:
self.fmt = fmt
def __repr__(self):
if hasattr(self, 'fmt'):
return f'{self:{self.fmt}}'
return float.__repr__(self)
#contextmanager
def formatted_floats():
c_make_encoder = json.encoder.c_make_encoder
json_float = json.encoder.float
json.encoder.c_make_encoder = None
json.encoder.float = FormattedFloat
try:
yield
finally:
json.encoder.c_make_encoder = c_make_encoder
json.encoder.float = json_float
Example
x = 12345.6789
d = dict(
a=x,
b=FormattedFloat(x),
c=FormattedFloat(x, '.4g'),
d=FormattedFloat(x, '.08f'),
)
>>> d
{'a': 12345.6789, 'b': 12345.6789, 'c': 1.235e+04, 'd': 12345.67890000}
Now,
with formatted_floats():
out = json.dumps(d)
>>> out
'{"a": 12345.6789, "b": 12345.6789, "c": 1.235e+04, "d": 12345.67890000}'
>>> json.loads(out)
{'a': 12345.6789, 'b': 12345.6789, 'c': 12350.0, 'd': 12345.6789}
Note that the original json.encoder attributes are restored by the context manager, so:
>>> json.dumps(d)
'{"a": 12345.6789, "b": 12345.6789, "c": 12345.6789, "d": 12345.6789}'

python: serialize a dictionary into a simple html output

using app engine - yes i know all about django templates and other template engines.
Lets say i have a dictionary or a simple object, i dont know its structure and i want to serialize it into html.
so if i had
{'data':{'id':1,'title':'home','address':{'street':'some road','city':'anycity','postal':'somepostal'}}}
want i want is that rendered in some form of readable html using lists or tables;
data:
id:1
title:home
address:
street: some road
city: anycity
postal:somepostal
now i know i can do
for key in dict.items
print dict[key]
but that wont dive into the child values and list each key, value pair when the key/value is a dictionary - ie the address dict.
Is their a module for python that is lightweight/fast that will do this nicely. or does anyone have any simple code they can paste that might do this.
Solution
All the solutions here were useful. pprint is no doubt the more stable means of printing the dictionary, though it falls short of returning anything near html. Though still printable.
I ended up with this for now:
def printitems(dictObj, indent=0):
p=[]
p.append('<ul>\n')
for k,v in dictObj.iteritems():
if isinstance(v, dict):
p.append('<li>'+ k+ ':')
p.append(printitems(v))
p.append('</li>')
else:
p.append('<li>'+ k+ ':'+ v+ '</li>')
p.append('</ul>\n')
return '\n'.join(p)
It converts the dict into unordered lists which is ok for now. some css and perhaps a little tweaking should make it readable.
Im going to reward the answer to the person that wrote the above code, i made a couple of small changes as the unordered lists were not nesting. I hope all agree that many of the solutions offered proved useful, But the above code renders a true html representation of a dictionary, even if crude.
The example made by pyfunc could easily be modified to generate simple nested html lists.
z = {'data':{'id':1,'title':'home','address':{'street':'some road','city':'anycity','postal':'somepostal'}}}
def printItems(dictObj, indent):
print ' '*indent + '<ul>\n'
for k,v in dictObj.iteritems():
if isinstance(v, dict):
print ' '*indent , '<li>', k, ':', '</li>'
printItems(v, indent+1)
else:
print ' '*indent , '<li>', k, ':', v, '</li>'
print ' '*indent + '</ul>\n'
printItems(z,0)
Not terribly pretty of course, but somewhere to start maybe. If all you want to do is visualize data, the pprint module really is good enough. You could just use the "pre" tag on the result from pprint and put that on your web page.
the pprint version would look something like this:
import pprint
z = {'data':{'id':1,'title':'home','address':{'street':'some road','city':'anycity','postal':'somepostal'}}}
print '<pre>', pprint.pformat(z), '</pre>'
And the html output look something like this:
{'data': {'address': {'city': 'anycity',
'postal': 'somepostal',
'street': 'some road'},
'id': 1,
'title': 'home'}}
Which isn't that pretty, but it at least shows the data in a more structured way.
import pprint
pprint.pprint(yourDict)
Well, no HTML, but similar to your for/print approach.
EDIT: or use:
niceText = pprint.pformat(yourDict)
this will give you the same nice output with all indents, etc. Now you can iterate over lines and format it into HTML:
htmlLines = []
for textLine in pprint.pformat(yourDict).splitlines():
htmlLines.append('<br/>%s' % textLine) # or something even nicer
htmlText = '\n'.join(htmlLines)
Here's my simple solution, It can handle any level of nested dictionary.
import json
temp_text = {'decision': {'date_time': None, 'decision_type': None},
'not_received': {'date_time': '2019-04-15T19:18:43.825766'},
'received': {'date_time': None},
'rfi': {'date_time': None},
'under_review': {'date_time': None}}
dict_text_for_html = json.dumps(
temp_text, indent=4
).replace(' ', '&nbsp').replace(',\n', ',<br>').replace('\n', '<br>')
html view of python dict
I needed something similar, but also wanted to pretty print lists, and lists inside the dict. Here's what I came up:
def format(self, obj, indent = 1):
if isinstance(obj, list):
htmls = []
for k in obj:
htmls.append(self.format(k,indent+1))
return '[<div style="margin-left: %dem">%s</div>]' % (indent, ',<br>'.join(htmls))
if isinstance(obj, dict):
htmls = []
for k,v in obj.iteritems():
htmls.append("<span style='font-style: italic; color: #888'>%s</span>: %s" % (k,self.format(v,indent+1)))
return '{<div style="margin-left: %dem">%s</div>}' % (indent, ',<br>'.join(htmls))
return str(obj)
Then, if you're using webapp on appengine, you can just do the following:
self.response.out.write(self.format(obj))
This is an example of the output:
Look at my implementation:
def pretty_items(r, d, nametag="<strong>%s: </strong>", itemtag='<li>%s</li>',
valuetag="%s", blocktag=('<ul>', '</ul>')):
if isinstance(d, dict):
r.append(blocktag[0])
for k, v in d.iteritems():
name = nametag % k
if isinstance(v, dict) or isinstance(v, list):
r.append(itemtag % name)
pretty_items(r, v)
else:
value = valuetag % v
r.append(itemtag % (name + value))
r.append(blocktag[1])
elif isinstance(d, list):
r.append(blocktag[0])
for i in d:
if isinstance(i, dict) or isinstance(i, list):
r.append(itemtag % " - ")
pretty_items(r, i)
else:
r.append(itemtag % i)
r.append(blocktag[1])
Will output all items in HTML format using <ul> and <li> tags. And is also optional to change the tags. And then, just use CSS to handle with the indentation.
None of the above examples give good results, so I wrote two of my own functions that create beautiful looking html output for dictionaries.
def dict_to_html(dd, level=0):
"""
Convert dict to html using basic html tags
"""
import simplejson
text = ''
for k, v in dd.iteritems():
text += '<br>' + ' '*(4*level) + '<b>%s</b>: %s' % (k, dict_to_html(v, level+1) if isinstance(v, dict) else (simplejson.dumps(v) if isinstance(v, list) else v))
return text
def dict_to_html_ul(dd, level=0):
"""
Convert dict to html using ul/li tags
"""
import simplejson
text = '<ul>'
for k, v in dd.iteritems():
text += '<li><b>%s</b>: %s</li>' % (k, dict_to_html_ul(v, level+1) if isinstance(v, dict) else (simplejson.dumps(v) if isinstance(v, list) else v))
text += '</ul>'
return text
You could use pretty print (pprint)
or if you want to do some further processing of display then you have to run through the dict yourself.
Be warned that the code is crude and will require numerous refinements. Solution uses recursion too, which is bad, if the recursion depth is higher.
z = {'data':{'id':1,'title':'home','address':{'street':'some road','city':'anycity','postal':'somepostal', 'telephone':{'home':'xxx','offie':'yyy'}}}}
def printItems(dictObj, indent):
it = dictObj.iteritems()
for k,v in it:
if isinstance(v, dict):
print ' '*indent , k, ':'
printItems(v, indent+1)
else:
print ' '*indent , k, ':', v
printItems(z,0)
Output:
data :
address :
city : anycity
postal : somepostal
street : some road
telephone :
home : xxx
offie : yyy
id : 1
title : home
Here is my version with support of lists (labels are verbose names of keys in dictionary):
def render_value(value, labels):
if isinstance(value, (list, tuple)):
return render_list(value, labels)
elif isinstance(value, dict):
return render_dict(value, labels)
else:
return value
def render_list(lst, labels):
items = [
'<li>%s</li>' % render_value(value, labels)
for value in lst
]
return '\n'.join(['\n<ul>'] + items + ['</ul>\n'])
def render_dict(dct, labels):
items = []
for key, value in dct.items():
if not value: continue
key = labels.get(key, key)
value = render_value(value, labels)
items.append('<li><b>%s</b>: %s</li>' % (key, value))
return '\n'.join(['\n<ul>'] + items + ['</ul>\n'])
imagine we have this :{name: "a", children:[{name: "b", children: [] },{..},{..}]
def ConvertDictToUlLi():
jsonResult = GetSomeRecursiveDict()
def CreateHtml(DictItem, output):
output = "<li>"+DictItem["name"] if jsonResult.has_key("name") else " "
if len(DictItem["children"]) > 0:
output = output + "<ul>"
for item in DictItem["children"]:
output = output + " "+CreateHtml(item, output)+" "
output = output + "</ul>"
return output+"</li>"
result = "<ul class='tree'>"+CreateHtml(jsonResult, "")+"</ul>"
return result

Categories