Package Products :: Package ZenUtils :: Module jsonutils |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.jsonutils

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  import json as _json
     15  import re
     16
     17 -def _recursiveCaster(ob):
     18      if isinstance(ob, dict):
     19          result = {}
     20          for k, v in ob.iteritems():
     21              result[str(k)] = _recursiveCaster(v)
     22          return result
     23      elif isinstance(ob, list):
     24          return [_recursiveCaster(x) for x in ob]
     25      elif isinstance(ob, unicode):
     26          return str(ob)
     27      else:
     28          return ob
     29
     30
     31 -class StringifyingDecoder(_json.JSONDecoder):
     32      """
     33      Casts all unicode objects as strings. This is necessary until Zope is less
     34      stupid.
     35      """
     36 -    def decode(self, s):
     37          result = super(StringifyingDecoder, self).decode(s)
     38          return _recursiveCaster(result)
     39
     40 -class JavaScript(object):
     41      """A simple class that represents a JavaScript literal that should not be JSON encoded."""
     42 -    def __init__(self, value):
     43          self.value = value
     44
     45 -    def __str__(self):
     46          return self.value
     47
     48 -class JavaScriptRegex(JavaScript):
     49      """A simple class that represents a JavaScript Regex literal that should not be JSON encoded."""
     50 -    def __str__(self):
     51          return '/' + self.value + '/'
     52
     53 -class JavaScriptEncoder(_json.JSONEncoder):
     54      """A JavaScript encoder based on JSON. It encodes like normal JSON except it passes JavaScript objects un-encoded."""
     55
     56      _js_start = '__js_start__'
     57      _js_end = '__js_end__'
     58      _js_re = re.compile(r'\["%s", (.*?), "%s"\]' % (_js_start, _js_end))
     59
     60 -    def default(self, obj):
     61          if isinstance(obj, JavaScript):
     62              return [self._js_start, str(obj), self._js_end]
     63          else:
     64              return _json.JSONEncoder.default(self, obj)
     65
     66 -    def _js_clean(self, jsonstr):
     67          # This re replace is not ideal but at least the dirtyness of it is encapsulated in these classes
     68          # instead of plain str manipulation being done in the wild.
     69          def fix(matchobj):
     70              return _json.loads(matchobj.group(1))
     71
     72          return self._js_re.sub(fix, jsonstr)
     73
     74 -    def encode(self, obj):
     75          return self._js_clean(_json.JSONEncoder.encode(self, obj))
     76
     77 -def json(value, **kw):
     78      """
     79      Serialize C{value} into a JSON string.
     80
     81      If C{value} is callable, a decorated version of C{value} that serializes its
     82      return value will be returned.
     83
     84          >>> value = (dict(a=1L), u"123", 123)
     85          >>> print json(value)
     86          [{"a": 1}, "123", 123]
     87          >>> @json
     88          ... def f():
     89          ...     return value
     90          ...
     91          >>> print f()
     92          [{"a": 1}, "123", 123]
     93
     94      @param value: An object to be serialized
     95      @type value: dict, list, tuple, str, etc. or callable
     96      @return: The JSON representation of C{value} or a decorated function
     97      @rtype: str, func
     98      """
     99      if callable(value):
    100          # Decorate the given callable
    101          def inner(*args, **kwargs):
    102              return _json.dumps(value(*args, **kwargs))
    103          # Well-behaved decorators look like the decorated function
    104          inner.__name__ = value.__name__
    105          inner.__dict__.update(value.__dict__)
    106          inner.__doc__ = value.__doc__
    107          return inner
    108      else:
    109          # Simply serialize the value passed
    110          return _json.dumps(value, **kw)
    111
    112 -def javascript(data):
    113      """A JavaScript encoder based on JSON. It encodes like normal JSON except it passes JavaScript objects un-encoded."""
    114      return json(data, cls=JavaScriptEncoder)
    115
    116 -def unjson(value, **kw):
    117      """
    118      Create the Python object represented by the JSON string C{value}.
    119
    120          >>> jsonstr = '[{"a": 1}, "123", 123]'
    121          >>> print unjson(jsonstr)
    122          [{'a': 1}, '123', 123]
    123
    124      @param value: A JSON string
    125      @type value: str
    126      @return: The object represented by C{value}
    127      """
    128      if 'cls' not in kw:
    129          kw['cls'] = StringifyingDecoder
    130      return _json.loads(value, **kw)
    131