/app/api/jsonify.py

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