/jsonify.py

https://github.com/kamens/gae_bingo · Python · 143 lines · 100 code · 29 blank · 14 comment · 28 complexity · 4ef88428d44c42b0d937e6a057514fa9 MD5 · raw file

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