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