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

Source Code for Module Products.ZenUtils.jsonutils

  1  ############################################################################## 
  2  #  
  3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved. 
  4  #  
  5  # This content is made available according to terms specified in 
  6  # License.zenoss under the directory where your Zenoss product is installed. 
  7  #  
  8  ############################################################################## 
  9   
 10   
 11  import json as _json 
 12  import re 
 13  from array import array 
 14   
15 -def _recursiveCaster(ob):
16 if isinstance(ob, dict): 17 result = {} 18 for k, v in ob.iteritems(): 19 result[str(k)] = _recursiveCaster(v) 20 return result 21 elif isinstance(ob, list): 22 return [_recursiveCaster(x) for x in ob] 23 elif isinstance(ob, unicode): 24 return str(ob) 25 else: 26 return ob
27 28
29 -class StringifyingDecoder(_json.JSONDecoder):
30 """ 31 Casts all unicode objects as strings. This is necessary until Zope is less 32 stupid. 33 """
34 - def decode(self, s):
35 result = super(StringifyingDecoder, self).decode(s) 36 return _recursiveCaster(result)
37
38 -class JavaScript(object):
39 """A simple class that represents a JavaScript literal that should not be JSON encoded."""
40 - def __init__(self, value):
41 self.value = value
42
43 - def __str__(self):
44 return self.value
45
46 -class JavaScriptRegex(JavaScript):
47 """A simple class that represents a JavaScript Regex literal that should not be JSON encoded."""
48 - def __str__(self):
49 return '/' + self.value + '/'
50
51 -class ObjectEncoder(_json.JSONEncoder):
52 _array_converters = { 'c':array.tostring, 53 'u':array.tounicode, 54 }
55 - def default(self, obj):
56 if hasattr(obj, '__json__') and callable(obj.__json__): 57 return obj.__json__() 58 if isinstance(obj, array): 59 return self._array_converters.get(obj.typecode, array.tolist)(obj) 60 return super(ObjectEncoder,self).default(obj)
61
62 -class JavaScriptEncoder(ObjectEncoder):
63 """A JavaScript encoder based on JSON. It encodes like normal JSON except it passes JavaScript objects un-encoded.""" 64 65 _js_start = '__js_start__' 66 _js_end = '__js_end__' 67 _js_re = re.compile(r'\["%s", (.*?), "%s"\]' % (_js_start, _js_end)) 68
69 - def default(self, obj):
70 if isinstance(obj, JavaScript): 71 return [self._js_start, str(obj), self._js_end] 72 73 return super(JavaScriptEncoder,self).default(obj)
74
75 - def _js_clean(self, jsonstr):
76 # This re replace is not ideal but at least the dirtyness of it is encapsulated in these classes 77 # instead of plain str manipulation being done in the wild. 78 def fix(matchobj): 79 return _json.loads(matchobj.group(1))
80 81 return self._js_re.sub(fix, jsonstr)
82
83 - def encode(self, obj):
84 return self._js_clean(super(JavaScriptEncoder,self).encode(obj))
85
86 -def _sanitize_value(value, errors='replace'):
87 """ 88 JSONEncoder doesn't allow overriding the encoding of built-in types 89 (in particular strings), and allows specifying an encoding but not 90 a policy for errors when decoding strings to UTF-8. This function 91 replaces all strings in a nested collection with unicode strings 92 with 'replace' as the error policy. 93 """ 94 newvalue = value 95 if isinstance(value,str): 96 newvalue = value.decode('utf8', errors) 97 elif isinstance(value, dict): 98 newvalue = {} 99 for k, v in value.iteritems(): 100 if isinstance(v, (str,set,list,dict,tuple)): 101 newvalue[k] = _sanitize_value(v) 102 else: 103 newvalue[k] = v 104 elif isinstance(value,(list,tuple)): 105 newvalue = [] 106 for v in value: 107 if isinstance(v, (str,set,list,dict,tuple)): 108 newvalue.append(_sanitize_value(v)) 109 else: 110 newvalue.append(v) 111 elif isinstance(value,set): 112 newvalue = set() 113 for v in value: 114 if isinstance(v, (str,set,list,dict,tuple)): 115 newvalue.add(_sanitize_value(v)) 116 else: 117 newvalue.add(v) 118 119 return newvalue
120
121 -def json(value, **kw):
122 """ 123 Serialize C{value} into a JSON string. 124 125 If C{value} is callable, a decorated version of C{value} that serializes its 126 return value will be returned. 127 128 >>> value = (dict(a=1L), u"123", 123) 129 >>> print json(value) 130 [{"a": 1}, "123", 123] 131 >>> @json 132 ... def f(): 133 ... return value 134 ... 135 >>> print f() 136 [{"a": 1}, "123", 123] 137 >>> from array import array 138 >>> a1 = array('i', list(range(10))) 139 >>> a2 = array('c', 'XYZZY') 140 >>> a3 = (array('u',[unichr(i) for i in range(250,260)])) 141 >>> [json(s) for s in (a1, a2, a3)] 142 ['[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', '"XYZZY"', '"\\\\u00fa\\\\u00fb\\\\u00fc\\\\u00fd\\\\u00fe\\\\u00ff\\\\u0100\\\\u0101\\\\u0102\\\\u0103"'] 143 >>> json([a1, a2, a3]) 144 '[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "XYZZY", "\\\\u00fa\\\\u00fb\\\\u00fc\\\\u00fd\\\\u00fe\\\\u00ff\\\\u0100\\\\u0101\\\\u0102\\\\u0103"]' 145 >>> json({'properties' : [{ 'key' : 'a1', 'value' : a1 },{ 'key' : 'a2', 'value' : a2 },{ 'key' : 'a3', 'value' : a3 },] }) 146 '{"properties": [{"value": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key": "a1"}, {"value": "XYZZY", "key": "a2"}, {"value": "\\\\u00fa\\\\u00fb\\\\u00fc\\\\u00fd\\\\u00fe\\\\u00ff\\\\u0100\\\\u0101\\\\u0102\\\\u0103", "key": "a3"}]}' 147 148 @param value: An object to be serialized 149 @type value: dict, list, tuple, str, etc. or callable 150 @return: The JSON representation of C{value} or a decorated function 151 @rtype: str, func 152 """ 153 if callable(value): 154 # Decorate the given callable 155 def inner(*args, **kwargs): 156 return json(value(*args, **kwargs))
157 # Well-behaved decorators look like the decorated function 158 inner.__name__ = value.__name__ 159 inner.__dict__.update(value.__dict__) 160 inner.__doc__ = value.__doc__ 161 return inner 162 else: 163 # Simply serialize the value 164 try: 165 return _json.dumps(value, cls=ObjectEncoder, **kw) 166 except UnicodeDecodeError: 167 sanitized = _sanitize_value(value) 168 return _json.dumps(sanitized, cls=ObjectEncoder, **kw) 169
170 -def javascript(value):
171 """A JavaScript encoder based on JSON. It encodes like normal JSON except it passes JavaScript objects un-encoded.""" 172 if callable(value): 173 # Decorate the given callable 174 def inner(*args, **kwargs): 175 return javascript(value(*args, **kwargs))
176 # Well-behaved decorators look like the decorated function 177 inner.__name__ = value.__name__ 178 inner.__dict__.update(value.__dict__) 179 inner.__doc__ = value.__doc__ 180 return inner 181 else: 182 # Simply serialize the value passed 183 return _json.dumps(value, cls=JavaScriptEncoder) 184
185 -def unjson(value, **kw):
186 """ 187 Create the Python object represented by the JSON string C{value}. 188 189 >>> jsonstr = '[{"a": 1}, "123", 123]' 190 >>> print unjson(jsonstr) 191 [{'a': 1}, '123', 123] 192 193 @param value: A JSON string 194 @type value: str 195 @return: The object represented by C{value} 196 """ 197 if 'cls' not in kw: 198 kw['cls'] = StringifyingDecoder 199 return _json.loads(value, **kw)
200