PageRenderTime 12ms CodeModel.GetById 1ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/gdata/core.py

http://radioappz.googlecode.com/
Python | 275 lines | 238 code | 10 blank | 27 comment | 0 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
 17
 18# This module is used for version 2 of the Google Data APIs.
 19
 20
 21__author__ = 'j.s@google.com (Jeff Scudder)'
 22
 23
 24"""Provides classes and methods for working with JSON-C.
 25
 26This module is experimental and subject to backwards incompatible changes.
 27
 28  Jsonc: Class which represents JSON-C data and provides pythonic member
 29         access which is a bit cleaner than working with plain old dicts.
 30  parse_json: Converts a JSON-C string into a Jsonc object.
 31  jsonc_to_string: Converts a Jsonc object into a string of JSON-C.
 32"""
 33
 34
 35try:
 36  import simplejson
 37except ImportError:
 38  try:
 39    # Try to import from django, should work on App Engine
 40    from django.utils import simplejson 
 41  except ImportError:
 42    # Should work for Python2.6 and higher.
 43    import json as simplejson
 44
 45
 46def _convert_to_jsonc(x):
 47  """Builds a Jsonc objects which wraps the argument's members."""
 48
 49  if isinstance(x, dict):
 50    jsonc_obj = Jsonc()
 51    # Recursively transform all members of the dict.
 52    # When converting a dict, we do not convert _name items into private
 53    # Jsonc members.
 54    for key, value in x.iteritems():
 55      jsonc_obj._dict[key] = _convert_to_jsonc(value)
 56    return jsonc_obj
 57  elif isinstance(x, list):
 58    # Recursively transform all members of the list.
 59    members = []
 60    for item in x:
 61      members.append(_convert_to_jsonc(item))
 62    return members
 63  else:
 64    # Return the base object.
 65    return x
 66
 67
 68def parse_json(json_string):
 69  """Converts a JSON-C string into a Jsonc object.
 70  
 71  Args:
 72    json_string: str or unicode The JSON to be parsed.
 73  
 74  Returns:
 75    A new Jsonc object.
 76  """
 77
 78  return _convert_to_jsonc(simplejson.loads(json_string))
 79
 80
 81def jsonc_to_string(jsonc_obj):
 82  """Converts a Jsonc object into a string of JSON-C."""
 83
 84  return simplejson.dumps(_convert_to_object(jsonc_obj))
 85
 86
 87def prettify_jsonc(jsonc_obj, indentation=2):
 88  """Converts a Jsonc object to a pretified (intented) JSON string."""
 89
 90  return simplejson.dumps(_convert_to_object(jsonc_obj), indent=indentation)
 91
 92
 93
 94def _convert_to_object(jsonc_obj):
 95  """Creates a new dict or list which has the data in the Jsonc object.
 96  
 97  Used to convert the Jsonc object to a plain old Python object to simplify
 98  conversion to a JSON-C string.
 99
100  Args:
101    jsonc_obj: A Jsonc object to be converted into simple Python objects
102               (dicts, lists, etc.)
103
104  Returns:
105    Either a dict, list, or other object with members converted from Jsonc
106    objects to the corresponding simple Python object.
107  """
108
109  if isinstance(jsonc_obj, Jsonc):
110    plain = {}
111    for key, value in jsonc_obj._dict.iteritems():
112      plain[key] = _convert_to_object(value)
113    return plain
114  elif isinstance(jsonc_obj, list):
115    plain = []
116    for item in jsonc_obj:
117      plain.append(_convert_to_object(item))
118    return plain
119  else:
120    return jsonc_obj
121
122
123def _to_jsonc_name(member_name):
124  """Converts a Python style member name to a JSON-C style name.
125  
126  JSON-C uses camelCaseWithLower while Python tends to use
127  lower_with_underscores so this method converts as follows:
128
129  spam becomes spam
130  spam_and_eggs becomes spamAndEggs
131
132  Args:
133    member_name: str or unicode The Python syle name which should be
134                 converted to JSON-C style.
135
136  Returns: 
137    The JSON-C style name as a str or unicode.
138  """
139
140  characters = []
141  uppercase_next = False
142  for character in member_name:
143    if character == '_':
144      uppercase_next = True
145    elif uppercase_next:
146      characters.append(character.upper())
147      uppercase_next = False
148    else:
149      characters.append(character)
150  return ''.join(characters)
151
152
153class Jsonc(object):
154  """Represents JSON-C data in an easy to access object format.
155
156  To access the members of a JSON structure which looks like this:
157  {
158    "data": {
159      "totalItems": 800,
160      "items": [
161        {
162          "content": {
163            "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp"
164          },
165          "viewCount": 220101,
166          "commentCount": 22,
167          "favoriteCount": 201
168        }
169      ]
170    },
171    "apiVersion": "2.0"
172  }
173
174  You would do the following:
175  x = gdata.core.parse_json(the_above_string)
176  # Gives you 800
177  x.data.total_items
178  # Should be 22
179  x.data.items[0].comment_count
180  # The apiVersion is '2.0'
181  x.api_version
182
183  To create a Jsonc object which would produce the above JSON, you would do:
184  gdata.core.Jsonc(
185      api_version='2.0',
186      data=gdata.core.Jsonc(
187          total_items=800,
188          items=[
189              gdata.core.Jsonc(
190                  view_count=220101,
191                  comment_count=22,
192                  favorite_count=201,
193                  content={
194                      '1': ('rtsp://v5.cache3.c.youtube.com'
195                            '/CiILENy.../0/0/0/video.3gp')})]))
196  or
197  x = gdata.core.Jsonc()
198  x.api_version = '2.0'
199  x.data = gdata.core.Jsonc()
200  x.data.total_items = 800
201  x.data.items = []
202  # etc.
203
204  How it works:
205  The JSON-C data is stored in an internal dictionary (._dict) and the
206  getattr, setattr, and delattr methods rewrite the name which you provide
207  to mirror the expected format in JSON-C. (For more details on name
208  conversion see _to_jsonc_name.) You may also access members using
209  getitem, setitem, delitem as you would for a dictionary. For example
210  x.data.total_items is equivalent to x['data']['totalItems']
211  (Not all dict methods are supported so if you need something other than
212  the item operations, then you will want to use the ._dict member).
213
214  You may need to use getitem or the _dict member to access certain
215  properties in cases where the JSON-C syntax does not map neatly to Python
216  objects. For example the YouTube Video feed has some JSON like this:
217  "content": {"1": "rtsp://v5.cache3.c.youtube.com..."...}
218  You cannot do x.content.1 in Python, so you would use the getitem as
219  follows:
220  x.content['1']
221  or you could use the _dict member as follows:
222  x.content._dict['1']
223
224  If you need to create a new object with such a mapping you could use.
225
226  x.content = gdata.core.Jsonc(_dict={'1': 'rtsp://cache3.c.youtube.com...'})
227  """
228
229  def __init__(self, _dict=None, **kwargs):
230    json = _dict or {}
231    for key, value in kwargs.iteritems():
232      if key.startswith('_'):
233        object.__setattr__(self, key, value)
234      else:
235        json[_to_jsonc_name(key)] = _convert_to_jsonc(value)
236
237    object.__setattr__(self, '_dict', json)
238
239  def __setattr__(self, name, value):
240    if name.startswith('_'):
241      object.__setattr__(self, name, value)
242    else:
243      object.__getattribute__(
244          self, '_dict')[_to_jsonc_name(name)] = _convert_to_jsonc(value)
245
246  def __getattr__(self, name):
247    if name.startswith('_'):
248      object.__getattribute__(self, name)
249    else:
250      try:
251        return object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
252      except KeyError:
253        raise AttributeError(
254            'No member for %s or [\'%s\']' % (name, _to_jsonc_name(name)))
255
256
257  def __delattr__(self, name):
258    if name.startswith('_'):
259      object.__delattr__(self, name)
260    else:
261      try:
262        del object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
263      except KeyError:
264        raise AttributeError(
265            'No member for %s (or [\'%s\'])' % (name, _to_jsonc_name(name)))
266
267  # For container methods pass-through to the underlying dict.
268  def __getitem__(self, key):
269    return self._dict[key]
270
271  def __setitem__(self, key, value):
272    self._dict[key] = value
273
274  def __delitem__(self, key):
275    del self._dict[key]