/gdata/core.py

http://radioappz.googlecode.com/ · Python · 275 lines · 201 code · 19 blank · 55 comment · 14 complexity · b89bce7bc5058ce014750831f9e9f313 MD5 · raw file

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2010 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. # This module is used for version 2 of the Google Data APIs.
  17. __author__ = 'j.s@google.com (Jeff Scudder)'
  18. """Provides classes and methods for working with JSON-C.
  19. This module is experimental and subject to backwards incompatible changes.
  20. Jsonc: Class which represents JSON-C data and provides pythonic member
  21. access which is a bit cleaner than working with plain old dicts.
  22. parse_json: Converts a JSON-C string into a Jsonc object.
  23. jsonc_to_string: Converts a Jsonc object into a string of JSON-C.
  24. """
  25. try:
  26. import simplejson
  27. except ImportError:
  28. try:
  29. # Try to import from django, should work on App Engine
  30. from django.utils import simplejson
  31. except ImportError:
  32. # Should work for Python2.6 and higher.
  33. import json as simplejson
  34. def _convert_to_jsonc(x):
  35. """Builds a Jsonc objects which wraps the argument's members."""
  36. if isinstance(x, dict):
  37. jsonc_obj = Jsonc()
  38. # Recursively transform all members of the dict.
  39. # When converting a dict, we do not convert _name items into private
  40. # Jsonc members.
  41. for key, value in x.iteritems():
  42. jsonc_obj._dict[key] = _convert_to_jsonc(value)
  43. return jsonc_obj
  44. elif isinstance(x, list):
  45. # Recursively transform all members of the list.
  46. members = []
  47. for item in x:
  48. members.append(_convert_to_jsonc(item))
  49. return members
  50. else:
  51. # Return the base object.
  52. return x
  53. def parse_json(json_string):
  54. """Converts a JSON-C string into a Jsonc object.
  55. Args:
  56. json_string: str or unicode The JSON to be parsed.
  57. Returns:
  58. A new Jsonc object.
  59. """
  60. return _convert_to_jsonc(simplejson.loads(json_string))
  61. def jsonc_to_string(jsonc_obj):
  62. """Converts a Jsonc object into a string of JSON-C."""
  63. return simplejson.dumps(_convert_to_object(jsonc_obj))
  64. def prettify_jsonc(jsonc_obj, indentation=2):
  65. """Converts a Jsonc object to a pretified (intented) JSON string."""
  66. return simplejson.dumps(_convert_to_object(jsonc_obj), indent=indentation)
  67. def _convert_to_object(jsonc_obj):
  68. """Creates a new dict or list which has the data in the Jsonc object.
  69. Used to convert the Jsonc object to a plain old Python object to simplify
  70. conversion to a JSON-C string.
  71. Args:
  72. jsonc_obj: A Jsonc object to be converted into simple Python objects
  73. (dicts, lists, etc.)
  74. Returns:
  75. Either a dict, list, or other object with members converted from Jsonc
  76. objects to the corresponding simple Python object.
  77. """
  78. if isinstance(jsonc_obj, Jsonc):
  79. plain = {}
  80. for key, value in jsonc_obj._dict.iteritems():
  81. plain[key] = _convert_to_object(value)
  82. return plain
  83. elif isinstance(jsonc_obj, list):
  84. plain = []
  85. for item in jsonc_obj:
  86. plain.append(_convert_to_object(item))
  87. return plain
  88. else:
  89. return jsonc_obj
  90. def _to_jsonc_name(member_name):
  91. """Converts a Python style member name to a JSON-C style name.
  92. JSON-C uses camelCaseWithLower while Python tends to use
  93. lower_with_underscores so this method converts as follows:
  94. spam becomes spam
  95. spam_and_eggs becomes spamAndEggs
  96. Args:
  97. member_name: str or unicode The Python syle name which should be
  98. converted to JSON-C style.
  99. Returns:
  100. The JSON-C style name as a str or unicode.
  101. """
  102. characters = []
  103. uppercase_next = False
  104. for character in member_name:
  105. if character == '_':
  106. uppercase_next = True
  107. elif uppercase_next:
  108. characters.append(character.upper())
  109. uppercase_next = False
  110. else:
  111. characters.append(character)
  112. return ''.join(characters)
  113. class Jsonc(object):
  114. """Represents JSON-C data in an easy to access object format.
  115. To access the members of a JSON structure which looks like this:
  116. {
  117. "data": {
  118. "totalItems": 800,
  119. "items": [
  120. {
  121. "content": {
  122. "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp"
  123. },
  124. "viewCount": 220101,
  125. "commentCount": 22,
  126. "favoriteCount": 201
  127. }
  128. ]
  129. },
  130. "apiVersion": "2.0"
  131. }
  132. You would do the following:
  133. x = gdata.core.parse_json(the_above_string)
  134. # Gives you 800
  135. x.data.total_items
  136. # Should be 22
  137. x.data.items[0].comment_count
  138. # The apiVersion is '2.0'
  139. x.api_version
  140. To create a Jsonc object which would produce the above JSON, you would do:
  141. gdata.core.Jsonc(
  142. api_version='2.0',
  143. data=gdata.core.Jsonc(
  144. total_items=800,
  145. items=[
  146. gdata.core.Jsonc(
  147. view_count=220101,
  148. comment_count=22,
  149. favorite_count=201,
  150. content={
  151. '1': ('rtsp://v5.cache3.c.youtube.com'
  152. '/CiILENy.../0/0/0/video.3gp')})]))
  153. or
  154. x = gdata.core.Jsonc()
  155. x.api_version = '2.0'
  156. x.data = gdata.core.Jsonc()
  157. x.data.total_items = 800
  158. x.data.items = []
  159. # etc.
  160. How it works:
  161. The JSON-C data is stored in an internal dictionary (._dict) and the
  162. getattr, setattr, and delattr methods rewrite the name which you provide
  163. to mirror the expected format in JSON-C. (For more details on name
  164. conversion see _to_jsonc_name.) You may also access members using
  165. getitem, setitem, delitem as you would for a dictionary. For example
  166. x.data.total_items is equivalent to x['data']['totalItems']
  167. (Not all dict methods are supported so if you need something other than
  168. the item operations, then you will want to use the ._dict member).
  169. You may need to use getitem or the _dict member to access certain
  170. properties in cases where the JSON-C syntax does not map neatly to Python
  171. objects. For example the YouTube Video feed has some JSON like this:
  172. "content": {"1": "rtsp://v5.cache3.c.youtube.com..."...}
  173. You cannot do x.content.1 in Python, so you would use the getitem as
  174. follows:
  175. x.content['1']
  176. or you could use the _dict member as follows:
  177. x.content._dict['1']
  178. If you need to create a new object with such a mapping you could use.
  179. x.content = gdata.core.Jsonc(_dict={'1': 'rtsp://cache3.c.youtube.com...'})
  180. """
  181. def __init__(self, _dict=None, **kwargs):
  182. json = _dict or {}
  183. for key, value in kwargs.iteritems():
  184. if key.startswith('_'):
  185. object.__setattr__(self, key, value)
  186. else:
  187. json[_to_jsonc_name(key)] = _convert_to_jsonc(value)
  188. object.__setattr__(self, '_dict', json)
  189. def __setattr__(self, name, value):
  190. if name.startswith('_'):
  191. object.__setattr__(self, name, value)
  192. else:
  193. object.__getattribute__(
  194. self, '_dict')[_to_jsonc_name(name)] = _convert_to_jsonc(value)
  195. def __getattr__(self, name):
  196. if name.startswith('_'):
  197. object.__getattribute__(self, name)
  198. else:
  199. try:
  200. return object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
  201. except KeyError:
  202. raise AttributeError(
  203. 'No member for %s or [\'%s\']' % (name, _to_jsonc_name(name)))
  204. def __delattr__(self, name):
  205. if name.startswith('_'):
  206. object.__delattr__(self, name)
  207. else:
  208. try:
  209. del object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
  210. except KeyError:
  211. raise AttributeError(
  212. 'No member for %s (or [\'%s\'])' % (name, _to_jsonc_name(name)))
  213. # For container methods pass-through to the underlying dict.
  214. def __getitem__(self, key):
  215. return self._dict[key]
  216. def __setitem__(self, key, value):
  217. self._dict[key] = value
  218. def __delitem__(self, key):
  219. del self._dict[key]