1 29 30 package com.caucho.quercus.lib.json; 31 32 import com.caucho.quercus.env.*; 33 import com.caucho.util.L10N; 34 35 class JsonDecoder { 36 private static final L10N L = new L10N(JsonDecoder.class); 37 38 private StringValue _string; 39 private int _len; 40 private int _offset; 41 42 private boolean _isAssociative; 43 44 public Value jsonDecode(Env env, 45 StringValue s, 46 boolean assoc) 47 { 48 _string = s; 49 _len = _string.length(); 50 _offset = 0; 51 52 _isAssociative = assoc; 53 54 Value val = jsonDecodeImpl(env); 55 56 if (skipWhitespace() >= 0) 58 return errorReturn(env, "expected no more input"); 59 60 return val; 61 } 62 63 68 private Value jsonDecodeImpl(Env env) 69 { 70 for (int ch = skipWhitespace(); ch >= 0; ch = read()) { 71 72 switch (ch) { 73 case '"': 74 return decodeString(env); 75 76 case 't': 77 if (read() == 'r' && 78 read() == 'u' && 79 read() == 'e') 80 return BooleanValue.TRUE; 81 else 82 return errorReturn(env, "expected 'true'"); 83 84 case 'f': 85 if (read() == 'a' && 86 read() == 'l' && 87 read() == 's' && 88 read() == 'e') 89 return BooleanValue.FALSE; 90 else 91 return errorReturn(env, "expected 'false'"); 92 93 case 'n': 94 if (read() == 'u' && 95 read() == 'l' && 96 read() == 'l') 97 return NullValue.NULL; 98 else 99 return errorReturn(env, "expected 'null'"); 100 101 case '[': 102 return decodeArray(env); 103 104 case '{': 105 return decodeObject(env); 106 107 default: 108 if (ch == '-' || ('0' <= ch && ch <= '9')) 109 return decodeNumber(env, ch); 110 else 111 return errorReturn(env); 112 } 113 } 114 115 return errorReturn(env); 116 } 117 118 121 private Value decodeNumber(Env env, int ch) 122 { 123 StringBuilder sb = new StringBuilder (); 124 125 if (ch == '-') { 127 sb.append((char)ch); 128 ch = read(); 129 } 130 131 if (ch >= 0) { 133 if (ch == '0') { 134 sb.append((char)ch); 135 ch = read(); 136 } 137 else if ('1' <= ch && ch <= '9') { 138 sb.append((char)ch); 139 ch = read(); 140 141 while ('0' <= ch && ch <= '9') { 142 sb.append((char)ch); 143 ch = read(); 144 } 145 } 146 else 147 return errorReturn(env, "expected 1-9"); 148 } 149 150 int integerEnd = sb.length(); 151 152 if (ch == '.') { 154 sb.append((char)ch); 155 ch = read(); 156 157 while ('0' <= ch && ch <= '9') { 158 sb.append((char)ch); 159 ch = read(); 160 } 161 } 162 163 if (ch == 'e' || ch == 'E') { 165 sb.append((char)ch); 166 ch = read(); 167 168 if (ch == '+' || ch == '-') { 169 sb.append((char)ch); 170 ch = read(); 171 } 172 173 if ('0' <= ch && ch <= '9') { 174 sb.append((char)ch); 175 ch = read(); 176 177 while ('0' <= ch && ch <= '9') { 178 sb.append((char)ch); 179 ch = read(); 180 } 181 } 182 else 183 return errorReturn(env, "expected 0-9 exponent"); 184 } 185 186 unread(); 187 188 if (integerEnd != sb.length()) 189 return new DoubleValue(Double.parseDouble(sb.toString())); 190 else 191 return new LongValue(Long.parseLong(sb.toString())); 192 } 193 194 197 private Value decodeArray(Env env) 198 { 199 ArrayValueImpl array = new ArrayValueImpl(); 200 201 while (true) { 202 int ch = skipWhitespace(); 203 204 if (ch == ']') 205 break; 206 207 unread(); 208 209 array.append(jsonDecodeImpl(env)); 210 211 ch = skipWhitespace(); 212 213 if (ch == ',') { 214 } 215 else if (ch == ']') 216 break; 217 else 218 errorReturn(env, "expected either ',' or ']'"); 219 } 220 221 return array; 222 } 223 224 private Value decodeObject(Env env) 225 { 226 if (_isAssociative) 227 return decodeObjectToArray(env); 228 else 229 return decodeObjectToObject(env); 230 } 231 232 235 private Value decodeObjectToArray(Env env) 236 { 237 ArrayValue array = new ArrayValueImpl(); 238 239 while (true) { 240 int ch = skipWhitespace(); 241 242 if (ch == '}') 243 break; 244 245 unread(); 246 247 Value name = jsonDecodeImpl(env); 248 249 ch = skipWhitespace(); 250 251 if (ch != ':') 252 return errorReturn(env, "expected ':'"); 253 254 array.append(name, jsonDecodeImpl(env)); 255 256 ch = skipWhitespace(); 257 258 if (ch == ',') { 259 } 260 else if (ch == '}') 261 break; 262 else 263 return errorReturn(env, "expected either ',' or '}'"); 264 } 265 266 return array; 267 } 268 269 272 private Value decodeObjectToObject(Env env) 273 { 274 ObjectValue object = env.createObject(); 275 276 while (true) { 277 int ch = skipWhitespace(); 278 279 if (ch == '}') 280 break; 281 282 unread(); 283 284 Value name = jsonDecodeImpl(env); 285 286 ch = skipWhitespace(); 287 288 if (ch != ':') 289 return errorReturn(env, "expected ':'"); 290 291 object.putFieldInit(env, name.toString(), jsonDecodeImpl(env)); 292 293 ch = skipWhitespace(); 294 295 if (ch == ',') { 296 } 297 else if (ch == '}') 298 break; 299 else 300 return errorReturn(env, "expected either ',' or '}'"); 301 } 302 303 return object; 304 } 305 306 309 private Value decodeString(Env env) 310 { 311 StringBuilderValue sbv = new StringBuilderValue(); 312 313 for (int ch = read(); ch >= 0; ch = read()) { 314 315 switch (ch) { 316 case '\\': 318 ch = read(); 319 if (ch < 0) 320 return errorReturn(env, "invalid escape character"); 321 322 switch (ch) { 323 case '"': 324 sbv.append('"'); 325 break; 326 case '\\': 327 sbv.append('\\'); 328 break; 329 case '/': 330 sbv.append('/'); 331 break; 332 case 'b': 333 sbv.append('\b'); 334 break; 335 case 'f': 336 sbv.append('\f'); 337 break; 338 case 'n': 339 sbv.append('\n'); 340 break; 341 case 'r': 342 sbv.append('\r'); 343 break; 344 case 't': 345 sbv.append('\t'); 346 break; 347 case 'u': 348 case 'U': 349 int hex = 0; 350 351 for (int i = 0; i < 4; i++) { 352 hex = hex << 4; 353 ch = read(); 354 355 if ('0' <= ch && ch <= '9') 356 hex += ch - '0'; 357 else if (ch >= 'a' && ch <= 'f') 358 hex += ch - 'a' + 10; 359 else if (ch >= 'A' && ch <= 'F') 360 hex += ch - 'A' + 10; 361 else 362 return errorReturn(env, "invalid escaped hex character"); 363 } 364 365 sbv.append((char)hex); 366 367 } 368 break; 369 370 case '"': 371 return sbv; 372 373 default: 374 sbv.append((char)ch); 375 } 376 } 377 378 return errorReturn(env, "error decoding string"); 379 } 380 381 private Value errorReturn(Env env) 382 { 383 return errorReturn(env, null); 384 } 385 386 private Value errorReturn(Env env, String message) 387 { 388 int start; 389 int end; 390 391 if (_offset < _len) { 392 start = _offset - 1; 393 end = _offset; 394 } 395 else { 396 start = _len - 1; 397 end = _len; 398 } 399 400 String token = _string.substring(start, end).toString(); 401 402 if (message != null) 403 env.warning(L.l("error parsing '{0}': {1}", token, message)); 404 else 405 env.warning(L.l("error parsing '{0}'", token)); 406 407 return NullValue.NULL; 408 } 409 410 private void unread() 411 { 412 if (_offset > 0) 413 _offset--; 414 } 415 416 private int peek(int index) 417 { 418 if (0 <= index && index < _len) 419 return _string.charAt(index); 420 else 421 return -1; 422 } 423 424 private int read() 425 { 426 if (_offset < _len) 427 return _string.charAt(_offset++); 428 else 429 return -1; 430 } 431 432 private int skipWhitespace() 433 { 434 int ch = read(); 435 for (; ch >= 0; ch = read()) { 436 if (ch != ' ' && 437 ch != '\n' && 438 ch != '\r' && 439 ch != '\t') 440 break; 441 } 442 443 return ch; 444 } 445 } 446 | Popular Tags |