/gdata/core.py
http://radioappz.googlecode.com/ · Python · 275 lines · 201 code · 19 blank · 55 comment · 14 complexity · b89bce7bc5058ce014750831f9e9f313 MD5 · raw file
- #!/usr/bin/env python
- #
- # Copyright (C) 2010 Google Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- # This module is used for version 2 of the Google Data APIs.
- __author__ = 'j.s@google.com (Jeff Scudder)'
- """Provides classes and methods for working with JSON-C.
- This module is experimental and subject to backwards incompatible changes.
- Jsonc: Class which represents JSON-C data and provides pythonic member
- access which is a bit cleaner than working with plain old dicts.
- parse_json: Converts a JSON-C string into a Jsonc object.
- jsonc_to_string: Converts a Jsonc object into a string of JSON-C.
- """
- try:
- import simplejson
- except ImportError:
- try:
- # Try to import from django, should work on App Engine
- from django.utils import simplejson
- except ImportError:
- # Should work for Python2.6 and higher.
- import json as simplejson
- def _convert_to_jsonc(x):
- """Builds a Jsonc objects which wraps the argument's members."""
- if isinstance(x, dict):
- jsonc_obj = Jsonc()
- # Recursively transform all members of the dict.
- # When converting a dict, we do not convert _name items into private
- # Jsonc members.
- for key, value in x.iteritems():
- jsonc_obj._dict[key] = _convert_to_jsonc(value)
- return jsonc_obj
- elif isinstance(x, list):
- # Recursively transform all members of the list.
- members = []
- for item in x:
- members.append(_convert_to_jsonc(item))
- return members
- else:
- # Return the base object.
- return x
- def parse_json(json_string):
- """Converts a JSON-C string into a Jsonc object.
-
- Args:
- json_string: str or unicode The JSON to be parsed.
-
- Returns:
- A new Jsonc object.
- """
- return _convert_to_jsonc(simplejson.loads(json_string))
- def jsonc_to_string(jsonc_obj):
- """Converts a Jsonc object into a string of JSON-C."""
- return simplejson.dumps(_convert_to_object(jsonc_obj))
- def prettify_jsonc(jsonc_obj, indentation=2):
- """Converts a Jsonc object to a pretified (intented) JSON string."""
- return simplejson.dumps(_convert_to_object(jsonc_obj), indent=indentation)
- def _convert_to_object(jsonc_obj):
- """Creates a new dict or list which has the data in the Jsonc object.
-
- Used to convert the Jsonc object to a plain old Python object to simplify
- conversion to a JSON-C string.
- Args:
- jsonc_obj: A Jsonc object to be converted into simple Python objects
- (dicts, lists, etc.)
- Returns:
- Either a dict, list, or other object with members converted from Jsonc
- objects to the corresponding simple Python object.
- """
- if isinstance(jsonc_obj, Jsonc):
- plain = {}
- for key, value in jsonc_obj._dict.iteritems():
- plain[key] = _convert_to_object(value)
- return plain
- elif isinstance(jsonc_obj, list):
- plain = []
- for item in jsonc_obj:
- plain.append(_convert_to_object(item))
- return plain
- else:
- return jsonc_obj
- def _to_jsonc_name(member_name):
- """Converts a Python style member name to a JSON-C style name.
-
- JSON-C uses camelCaseWithLower while Python tends to use
- lower_with_underscores so this method converts as follows:
- spam becomes spam
- spam_and_eggs becomes spamAndEggs
- Args:
- member_name: str or unicode The Python syle name which should be
- converted to JSON-C style.
- Returns:
- The JSON-C style name as a str or unicode.
- """
- characters = []
- uppercase_next = False
- for character in member_name:
- if character == '_':
- uppercase_next = True
- elif uppercase_next:
- characters.append(character.upper())
- uppercase_next = False
- else:
- characters.append(character)
- return ''.join(characters)
- class Jsonc(object):
- """Represents JSON-C data in an easy to access object format.
- To access the members of a JSON structure which looks like this:
- {
- "data": {
- "totalItems": 800,
- "items": [
- {
- "content": {
- "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp"
- },
- "viewCount": 220101,
- "commentCount": 22,
- "favoriteCount": 201
- }
- ]
- },
- "apiVersion": "2.0"
- }
- You would do the following:
- x = gdata.core.parse_json(the_above_string)
- # Gives you 800
- x.data.total_items
- # Should be 22
- x.data.items[0].comment_count
- # The apiVersion is '2.0'
- x.api_version
- To create a Jsonc object which would produce the above JSON, you would do:
- gdata.core.Jsonc(
- api_version='2.0',
- data=gdata.core.Jsonc(
- total_items=800,
- items=[
- gdata.core.Jsonc(
- view_count=220101,
- comment_count=22,
- favorite_count=201,
- content={
- '1': ('rtsp://v5.cache3.c.youtube.com'
- '/CiILENy.../0/0/0/video.3gp')})]))
- or
- x = gdata.core.Jsonc()
- x.api_version = '2.0'
- x.data = gdata.core.Jsonc()
- x.data.total_items = 800
- x.data.items = []
- # etc.
- How it works:
- The JSON-C data is stored in an internal dictionary (._dict) and the
- getattr, setattr, and delattr methods rewrite the name which you provide
- to mirror the expected format in JSON-C. (For more details on name
- conversion see _to_jsonc_name.) You may also access members using
- getitem, setitem, delitem as you would for a dictionary. For example
- x.data.total_items is equivalent to x['data']['totalItems']
- (Not all dict methods are supported so if you need something other than
- the item operations, then you will want to use the ._dict member).
- You may need to use getitem or the _dict member to access certain
- properties in cases where the JSON-C syntax does not map neatly to Python
- objects. For example the YouTube Video feed has some JSON like this:
- "content": {"1": "rtsp://v5.cache3.c.youtube.com..."...}
- You cannot do x.content.1 in Python, so you would use the getitem as
- follows:
- x.content['1']
- or you could use the _dict member as follows:
- x.content._dict['1']
- If you need to create a new object with such a mapping you could use.
- x.content = gdata.core.Jsonc(_dict={'1': 'rtsp://cache3.c.youtube.com...'})
- """
- def __init__(self, _dict=None, **kwargs):
- json = _dict or {}
- for key, value in kwargs.iteritems():
- if key.startswith('_'):
- object.__setattr__(self, key, value)
- else:
- json[_to_jsonc_name(key)] = _convert_to_jsonc(value)
- object.__setattr__(self, '_dict', json)
- def __setattr__(self, name, value):
- if name.startswith('_'):
- object.__setattr__(self, name, value)
- else:
- object.__getattribute__(
- self, '_dict')[_to_jsonc_name(name)] = _convert_to_jsonc(value)
- def __getattr__(self, name):
- if name.startswith('_'):
- object.__getattribute__(self, name)
- else:
- try:
- return object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
- except KeyError:
- raise AttributeError(
- 'No member for %s or [\'%s\']' % (name, _to_jsonc_name(name)))
- def __delattr__(self, name):
- if name.startswith('_'):
- object.__delattr__(self, name)
- else:
- try:
- del object.__getattribute__(self, '_dict')[_to_jsonc_name(name)]
- except KeyError:
- raise AttributeError(
- 'No member for %s (or [\'%s\'])' % (name, _to_jsonc_name(name)))
- # For container methods pass-through to the underlying dict.
- def __getitem__(self, key):
- return self._dict[key]
- def __setitem__(self, key, value):
- self._dict[key] = value
- def __delitem__(self, key):
- del self._dict[key]