-
Notifications
You must be signed in to change notification settings - Fork 25
/
jsonify.py
143 lines (113 loc) · 4.39 KB
/
jsonify.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Based on http://appengine-cookbook.appspot.com/recipe/extended-jsonify-function-for-dbmodel,
# with modifications for flask and performance.
# use json in Python 2.7, fallback to simplejson for Python 2.5
try:
import json
except ImportError:
import simplejson as json
from google.appengine.ext import db
from datetime import datetime
import re
SIMPLE_TYPES = (int, long, float, bool, basestring)
def dumps(obj, camel_cased=False):
if isinstance(obj, SIMPLE_TYPES):
return obj
elif obj == None:
return None
elif isinstance(obj, list):
items = []
for item in obj:
items.append(dumps(item, camel_cased))
return items
elif isinstance(obj, datetime):
return obj.strftime("%Y-%m-%dT%H:%M:%SZ")
elif isinstance(obj, dict):
properties = {}
for key in obj:
value = dumps(obj[key], camel_cased)
if camel_cased:
properties[camel_casify(key)] = value
else:
properties[key] = value
return properties
properties = dict()
if isinstance(obj, db.Model):
properties['kind'] = obj.kind()
serialize_blacklist = []
if hasattr(obj, "_serialize_blacklist"):
serialize_blacklist = obj._serialize_blacklist
serialize_list = dir(obj)
if hasattr(obj, "_serialize_whitelist"):
serialize_list = obj._serialize_whitelist
for property in serialize_list:
if _is_visible_property(property, serialize_blacklist):
try:
value = obj.__getattribute__(property)
if not _is_visible_property_value(value):
continue
valueClass = str(value.__class__)
if is_visible_class_name(valueClass):
value = dumps(value, camel_cased)
if camel_cased:
properties[camel_casify(property)] = value
else:
properties[property] = value
except:
continue
if len(properties) == 0:
return str(obj)
else:
return properties
UNDERSCORE_RE = re.compile("_([a-z])")
def camel_case_replacer(match):
""" converts "_[a-z]" to remove the underscore and uppercase the letter """
return match.group(0)[1:].upper()
def camel_casify(str):
return re.sub(UNDERSCORE_RE, camel_case_replacer, str)
def _is_visible_property(property, serialize_blacklist):
return (property[0] != '_' and
not property.startswith("INDEX_") and
not property in serialize_blacklist)
def _is_visible_property_value(value):
# Right now only db.Blob objects are
# blacklisted (since they may contain binary that doesn't JSONify well)
if isinstance(value, db.Blob):
return False
return True
def is_visible_class_name(class_name):
return not(
('function' in class_name) or
('built' in class_name) or
('method' in class_name) or
('db.Query' in class_name)
)
class JSONModelEncoder(json.JSONEncoder):
def default(self, o):
""" Turns objects into serializable dicts for the default encoder """
return dumps(o)
class JSONModelEncoderCamelCased(json.JSONEncoder):
def encode(self, obj):
# We override encode() instead of the usual default(), since we need
# to handle built in types like lists and dicts ourselves as well.
# Specifically, we need to re-construct the object with camelCasing
# anyways, so do that before encoding.
obj = dumps(obj, camel_cased=True)
return super(self.__class__, self).encode(obj)
def jsonify(data, camel_cased=False):
"""jsonify data in a standard (human friendly) way. If a db.Model
entity is passed in it will be encoded as a dict.
If the current request being served is being served via Flask, and
has a parameter "casing" with the value "camel", properties in the
resulting output will be converted to use camelCase instead of the
regular Pythonic underscore convention.
"""
if camel_cased:
encoder = JSONModelEncoderCamelCased
else:
encoder = JSONModelEncoder
return json.dumps(data,
skipkeys=True,
sort_keys=False,
ensure_ascii=False,
indent=4,
cls=encoder)