diff --git a/ckan/controllers/api.py b/ckan/controllers/api.py index 23aec691ced..2f7eab631f6 100644 --- a/ckan/controllers/api.py +++ b/ckan/controllers/api.py @@ -83,7 +83,9 @@ def _finish(self, status_int, response_data=None, if response_data is not None: response.headers['Content-Type'] = CONTENT_TYPES[content_type] if content_type == 'json': - response_msg = h.json.dumps(response_data) + response_msg = h.json.dumps( + response_data, + for_json=True) # handle objects with for_json methods else: response_msg = response_data # Support "JSONP" callback. @@ -164,7 +166,8 @@ def action(self, logic_function, ver=None): _('Action name not known: %s') % logic_function) context = {'model': model, 'session': model.Session, 'user': c.user, - 'api_version': ver, 'auth_user_obj': c.userobj} + 'api_version': ver, 'return_type': 'LazyJSONObject', + 'auth_user_obj': c.userobj} model.Session()._context = context return_dict = {'help': h.url_for(controller='api', diff --git a/ckan/lib/lazyjson.py b/ckan/lib/lazyjson.py new file mode 100644 index 00000000000..6305cb7d894 --- /dev/null +++ b/ckan/lib/lazyjson.py @@ -0,0 +1,55 @@ +import simplejson as json +import simplejson.encoder as json_encoder + + +class LazyJSONObject(dict): + '''An object that behaves like a dict returned from json.loads''' + def __init__(self, json_string): + self._json_string = json_string + self._json_dict = None + + def _loads(self): + if not self._json_dict: + self._json_dict = json.loads(self._json_string) + self._json_string = None + return self._json_dict + + def __nonzero__(self): + return True + + def for_json(self): + if self._json_string: + return JSONString(self._json_string) + return self._json_dict + + +def _loads_method(name): + def method(self, *args, **kwargs): + return getattr(self._loads(), name)(*args, **kwargs) + return method + +for fn in ['__cmp__', '__contains__', '__delitem__', '__eq__', '__ge__', + '__getitem__', '__gt__', '__iter__', '__le__', '__len__', '__lt__', + '__ne__', '__setitem__', 'clear', 'copy', 'fromkeys', 'get', + 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', + 'pop', 'popitem', 'setdefault', 'update', 'values']: + setattr(LazyJSONObject, fn, _loads_method(fn)) + + +class JSONString(int): + ''' + A type for already-encoded JSON + + Fake-out simplejson by subclassing int so that simplejson calls + our __str__ method to produce JSON. + + This trick is unpleasant, but significantly less fragile than + subclassing JSONEncoder and modifying its internal workings, or + monkeypatching the simplejson library. + ''' + def __init__(self, s): + self.s = s + super(JSONString, self).__init__(-1) + + def __str__(self): + return s diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index 1a124f4eb85..a7512e0929e 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -285,7 +285,9 @@ def resource_create(context, data_dict): package_id = _get_or_bust(data_dict, 'package_id') _get_or_bust(data_dict, 'url') - pkg_dict = _get_action('package_show')(context, {'id': package_id}) + pkg_dict = _get_action('package_show')( + dict(context, return_type='dict'), + {'id': package_id}) _check_access('resource_create', context, data_dict) diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 1596563bb7c..908c1d738cf 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -24,6 +24,7 @@ import ckan.lib.activity_streams as activity_streams import ckan.lib.datapreview as datapreview import ckan.authz as authz +import ckan.lib.lazyjson as lazyjson from ckan.common import _ @@ -942,7 +943,11 @@ def package_show(context, data_dict): else: use_validated_cache = 'schema' not in context if use_validated_cache and 'validated_data_dict' in search_result: - package_dict = json.loads(search_result['validated_data_dict']) + package_json = search_result['validated_data_dict'] + if context.get('return_type') == 'LazyJSONObject': + package_dict = lazyjson.LazyJSONObject(package_json) + else: + package_dict = json.loads(package_json) package_dict_validated = True else: package_dict = json.loads(search_result['data_dict']) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index f87b81ad078..561813596e4 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -136,7 +136,8 @@ def resource_update(context, data_dict): del context["resource"] package_id = resource.package.id - pkg_dict = _get_action('package_show')(context, {'id': package_id}) + pkg_dict = _get_action('package_show')(dict(context, return_type='dict'), + {'id': package_id}) for n, p in enumerate(pkg_dict['resources']): if p['id'] == id: