Package scapi :: Module json
[hide private]
[frames] | no frames]

Source Code for Module scapi.json

  1  import string 
  2  import types 
  3   
  4  ##    json.py implements a JSON (http://json.org) reader and writer. 
  5  ##    Copyright (C) 2005  Patrick D. Logan 
  6  ##    Contact mailto:patrickdlogan@stardecisions.com 
  7  ## 
  8  ##    This library is free software; you can redistribute it and/or 
  9  ##    modify it under the terms of the GNU Lesser General Public 
 10  ##    License as published by the Free Software Foundation; either 
 11  ##    version 2.1 of the License, or (at your option) any later version. 
 12  ## 
 13  ##    This library is distributed in the hope that it will be useful, 
 14  ##    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  ##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 16  ##    Lesser General Public License for more details. 
 17  ## 
 18  ##    You should have received a copy of the GNU Lesser General Public 
 19  ##    License along with this library; if not, write to the Free Software 
 20  ##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 21   
 22   
23 -class _StringGenerator(object):
24 - def __init__(self, string):
25 self.string = string 26 self.index = -1
27 - def peek(self):
28 i = self.index + 1 29 if i < len(self.string): 30 return self.string[i] 31 else: 32 return None
33 - def next(self):
34 self.index += 1 35 if self.index < len(self.string): 36 return self.string[self.index] 37 else: 38 raise StopIteration
39 - def all(self):
40 return self.string
41
42 -class WriteException(Exception):
43 pass
44
45 -class ReadException(Exception):
46 pass
47
48 -class JsonReader(object):
49 hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15} 50 escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'} 51
52 - def read(self, s):
53 self._generator = _StringGenerator(s) 54 result = self._read() 55 return result
56
57 - def _read(self):
58 self._eatWhitespace() 59 peek = self._peek() 60 if peek is None: 61 raise ReadException, "Nothing to read: '%s'" % self._generator.all() 62 if peek == '{': 63 return self._readObject() 64 elif peek == '[': 65 return self._readArray() 66 elif peek == '"': 67 return self._readString() 68 elif peek == '-' or peek.isdigit(): 69 return self._readNumber() 70 elif peek == 't': 71 return self._readTrue() 72 elif peek == 'f': 73 return self._readFalse() 74 elif peek == 'n': 75 return self._readNull() 76 elif peek == '/': 77 self._readComment() 78 return self._read() 79 else: 80 raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
81
82 - def _readTrue(self):
83 self._assertNext('t', "true") 84 self._assertNext('r', "true") 85 self._assertNext('u', "true") 86 self._assertNext('e', "true") 87 return True
88
89 - def _readFalse(self):
90 self._assertNext('f', "false") 91 self._assertNext('a', "false") 92 self._assertNext('l', "false") 93 self._assertNext('s', "false") 94 self._assertNext('e', "false") 95 return False
96
97 - def _readNull(self):
98 self._assertNext('n', "null") 99 self._assertNext('u', "null") 100 self._assertNext('l', "null") 101 self._assertNext('l', "null") 102 return None
103
104 - def _assertNext(self, ch, target):
105 if self._next() != ch: 106 raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
107
108 - def _readNumber(self):
109 isfloat = False 110 result = self._next() 111 peek = self._peek() 112 while peek is not None and (peek.isdigit() or peek == "."): 113 isfloat = isfloat or peek == "." 114 result = result + self._next() 115 peek = self._peek() 116 try: 117 if isfloat: 118 return float(result) 119 else: 120 return int(result) 121 except ValueError: 122 raise ReadException, "Not a valid JSON number: '%s'" % result
123
124 - def _readString(self):
125 result = "" 126 assert self._next() == '"' 127 try: 128 while self._peek() != '"': 129 ch = self._next() 130 if ch == "\\": 131 ch = self._next() 132 if ch in 'brnft': 133 ch = self.escapes[ch] 134 elif ch == "u": 135 ch4096 = self._next() 136 ch256 = self._next() 137 ch16 = self._next() 138 ch1 = self._next() 139 n = 4096 * self._hexDigitToInt(ch4096) 140 n += 256 * self._hexDigitToInt(ch256) 141 n += 16 * self._hexDigitToInt(ch16) 142 n += self._hexDigitToInt(ch1) 143 ch = unichr(n) 144 elif ch not in '"/\\': 145 raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all()) 146 result = result + ch 147 except StopIteration: 148 raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all() 149 assert self._next() == '"' 150 return result
151
152 - def _hexDigitToInt(self, ch):
153 try: 154 result = self.hex_digits[ch.upper()] 155 except KeyError: 156 try: 157 result = int(ch) 158 except ValueError: 159 raise ReadException, "The character %s is not a hex digit." % ch 160 return result
161
162 - def _readComment(self):
163 assert self._next() == "/" 164 second = self._next() 165 if second == "/": 166 self._readDoubleSolidusComment() 167 elif second == '*': 168 self._readCStyleComment() 169 else: 170 raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
171
172 - def _readCStyleComment(self):
173 try: 174 done = False 175 while not done: 176 ch = self._next() 177 done = (ch == "*" and self._peek() == "/") 178 if not done and ch == "/" and self._peek() == "*": 179 raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all() 180 self._next() 181 except StopIteration: 182 raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
183
185 try: 186 ch = self._next() 187 while ch != "\r" and ch != "\n": 188 ch = self._next() 189 except StopIteration: 190 pass
191
192 - def _readArray(self):
193 result = [] 194 assert self._next() == '[' 195 done = self._peek() == ']' 196 while not done: 197 item = self._read() 198 result.append(item) 199 self._eatWhitespace() 200 done = self._peek() == ']' 201 if not done: 202 ch = self._next() 203 if ch != ",": 204 raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch) 205 assert ']' == self._next() 206 return result
207
208 - def _readObject(self):
209 result = {} 210 assert self._next() == '{' 211 done = self._peek() == '}' 212 while not done: 213 key = self._read() 214 if type(key) is not types.StringType: 215 raise ReadException, "Not a valid JSON object key (should be a string): %s" % key 216 self._eatWhitespace() 217 ch = self._next() 218 if ch != ":": 219 raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch) 220 self._eatWhitespace() 221 val = self._read() 222 result[key] = val 223 self._eatWhitespace() 224 done = self._peek() == '}' 225 if not done: 226 ch = self._next() 227 if ch != ",": 228 raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch) 229 assert self._next() == "}" 230 return result
231
232 - def _eatWhitespace(self):
233 p = self._peek() 234 while p is not None and p in string.whitespace or p == '/': 235 if p == '/': 236 self._readComment() 237 else: 238 self._next() 239 p = self._peek()
240
241 - def _peek(self):
242 return self._generator.peek()
243
244 - def _next(self):
245 return self._generator.next()
246
247 -class JsonWriter(object):
248
249 - def _append(self, s):
250 self._results.append(s)
251
252 - def write(self, obj, escaped_forward_slash=False):
253 self._escaped_forward_slash = escaped_forward_slash 254 self._results = [] 255 self._write(obj) 256 return "".join(self._results)
257
258 - def _write(self, obj):
259 ty = type(obj) 260 if ty is types.DictType: 261 n = len(obj) 262 self._append("{") 263 for k, v in obj.items(): 264 self._write(k) 265 self._append(":") 266 self._write(v) 267 n = n - 1 268 if n > 0: 269 self._append(",") 270 self._append("}") 271 elif ty is types.ListType or ty is types.TupleType: 272 n = len(obj) 273 self._append("[") 274 for item in obj: 275 self._write(item) 276 n = n - 1 277 if n > 0: 278 self._append(",") 279 self._append("]") 280 elif ty is types.StringType or ty is types.UnicodeType: 281 self._append('"') 282 obj = obj.replace('\\', r'\\') 283 if self._escaped_forward_slash: 284 obj = obj.replace('/', r'\/') 285 obj = obj.replace('"', r'\"') 286 obj = obj.replace('\b', r'\b') 287 obj = obj.replace('\f', r'\f') 288 obj = obj.replace('\n', r'\n') 289 obj = obj.replace('\r', r'\r') 290 obj = obj.replace('\t', r'\t') 291 self._append(obj) 292 self._append('"') 293 elif ty is types.IntType or ty is types.LongType: 294 self._append(str(obj)) 295 elif ty is types.FloatType: 296 self._append("%f" % obj) 297 elif obj is True: 298 self._append("true") 299 elif obj is False: 300 self._append("false") 301 elif obj is None: 302 self._append("null") 303 else: 304 raise WriteException, "Cannot write in JSON: %s" % repr(obj)
305
306 -def write(obj, escaped_forward_slash=False):
307 return JsonWriter().write(obj, escaped_forward_slash)
308
309 -def read(s):
310 return JsonReader().read(s)
311