1 // Author: Hong Jiang <hong@hjiang.net>
2 /*
3 source: http://github.com/hjiang/jsonxx/
4
5 Copyright (c) 2010 Hong Jiang
6
7 Permission is hereby granted, free of charge, to any person
8 obtaining a copy of this software and associated documentation
9 files (the "Software"), to deal in the Software without
10 restriction, including without limitation the rights to use,
11 copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the
13 Software is furnished to do so, subject to the following
14 conditions:
15
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 OTHER DEALINGS IN THE SOFTWARE.
27
28 */
29
30 #include "jsonxx.h"
31
32 #include <cctype>
33 #include <iostream>
34 #include <sstream>
35
36 #include <math.h>
37
38 #include "log.h"
39
40 namespace jsonxx {
41
eat_whitespaces(std::istream & input)42 void eat_whitespaces(std::istream& input) {
43 char ch;
44 do {
45 input.get(ch);
46 } while(isspace(ch));
47 input.putback(ch);
48 }
49
50 // Try to consume characters from the input stream and match the
51 // pattern string. Leading whitespaces from the input are ignored if
52 // ignore_ws is true.
match(const std::string & pattern,std::istream & input,bool ignore_ws)53 bool match(const std::string& pattern, std::istream& input,
54 bool ignore_ws) {
55 if (ignore_ws) {
56 eat_whitespaces(input);
57 }
58 std::string::const_iterator cur(pattern.begin());
59 char ch(0);
60 while(input && !input.eof() && cur != pattern.end()) {
61 input.get(ch);
62 if (ch != *cur) {
63 input.putback(ch);
64 return false;
65 } else {
66 cur++;
67 }
68 }
69 return cur == pattern.end();
70 }
71
parse_string(std::istream & input,std::string * value)72 bool parse_string(std::istream& input, std::string* value) {
73 if (!match("\"", input)) {
74 return false;
75 }
76 char ch;
77 while(!input.eof() && input.good()) {
78 input.get(ch);
79 if (ch == '"' ) {
80 break;
81 }
82 if (ch == '\\') {
83 if (input.eof())
84 return false;
85 char ch1;
86 input.get(ch1);
87 switch (ch1) {
88 case '"':
89 case '\\':
90 case '/': value->push_back(ch1); break;
91 case 'b': value->push_back('\b'); break;
92 case 'f': value->push_back('\f'); break;
93 case 'n': value->push_back('\n'); break;
94 case 'r': value->push_back('\r'); break;
95 case 't': value->push_back('\t'); break;
96 case 'u':
97 value->push_back('\\');
98 value->push_back('u');
99 break;
100 default: return false;
101 }
102 }
103 else value->push_back(ch);
104 }
105 if (input && ch == '"') {
106 return true;
107 } else {
108 return false;
109 }
110 }
111
parse_bool(std::istream & input,bool * value)112 bool parse_bool(std::istream& input, bool* value) {
113 if (match("true", input)) {
114 *value = true;
115 return true;
116 }
117 if (match("false", input)) {
118 *value = false;
119 return true;
120 }
121 return false;
122 }
123
parse_null(std::istream & input)124 bool parse_null(std::istream& input) {
125 if (match("null", input)) {
126 return true;
127 }
128 return false;
129 }
130
131 /*
132 bool parse_float(std::istream& input, double* value) {
133 eat_whitespaces(input);
134 char ch;
135 bool has_dot = false;
136 std::string value_str;
137 int sign = 1;
138 if (match("-", input)) {
139 sign = -1;
140 } else {
141 match("+", input);
142 }
143 while(input && !input.eof()) {
144 input.get(ch);
145 if (ch=='.')
146 has_dot = true;
147 if (!isdigit(ch) && (!(ch == '.'))) {
148 input.putback(ch);
149 break;
150 }
151 value_str.push_back(ch);
152 }
153 if (!has_dot) {
154 for (std::string::reverse_iterator r_it=
155 value_str.rbegin(); r_it != value_str.rend(); r_it++)
156 input.putback(*r_it);
157 return false;
158 }
159 if (value_str.size() > 0) {
160 std::istringstream(value_str) >> *value;
161 *value*=sign;
162 return true;
163 } else {
164 return false;
165 }
166 }
167 */
168
169
170 // bool parse_number(std::istream& input, long* value) {
171 // eat_whitespaces(input);
172 // char ch;
173 // std::string value_str;
174 // int sign = 1;
175 // if (match("-", input)) {
176 // sign = -1;
177 // } else {
178 // match("+", input);
179 // }
180 // while(input && !input.eof()) {
181 // input.get(ch);
182 // if (!isdigit(ch)) {
183 // input.putback(ch);
184 // break;
185 // }
186 // value_str.push_back(ch);
187 // }
188 // if (value_str.size() > 0) {
189 // std::istringstream(value_str) >> *value;
190 // *value*=sign;
191 // return true;
192 // } else {
193 // return false;
194 // }
195 // }
196
197
parse_number(std::istream & input,long * value)198 bool parse_number(std::istream& input, long* value) {
199 eat_whitespaces(input);
200 char ch;
201 std::string value_str;
202 std::string exp_str; // whole exp part
203 std::string exp_value_str; // value of exp part
204 int sign = 1;
205 int e_sign = 1;
206 bool correct = true;
207 long e_value;
208
209 enum {
210 p_number,
211 p_e,
212 p_enumber
213 } p_state = p_number;
214
215 if (match("-", input)) {
216 sign = -1;
217 } else {
218 match("+", input);
219 }
220
221 while(input && !input.eof()) {
222 input.get(ch);
223 switch (p_state) {
224 case p_number: {
225 // DBG("st = p_number, ch=%c\n",ch);
226 if (ch == 'E' || ch == 'e') {
227 exp_str.push_back(ch);
228 p_state = p_e;
229 continue;
230 }
231 if (!isdigit(ch)) {
232 input.putback(ch);
233 correct = false;
234 break;
235 }
236 value_str.push_back(ch);
237 } break;
238
239 case p_e: {
240 // DBG("st = p_e, ch=%c\n",ch);
241
242 if (ch == '+') {
243 exp_str.push_back(ch);
244 p_state = p_enumber;
245 } else if (ch == '-') {
246 e_sign = -1;
247 exp_str.push_back(ch);
248 p_state = p_enumber;
249 } else if (isdigit(ch)) {
250 exp_value_str.push_back(ch);
251 exp_str.push_back(ch);
252 p_state = p_enumber;
253 } else {
254 input.putback(ch);
255 correct = false;
256 }
257 } break;
258
259 case p_enumber: {
260 // DBG("st = p_enumber, ch=%c\n",ch);
261
262 if (isdigit(ch)) {
263 exp_value_str.push_back(ch);
264 exp_str.push_back(ch);
265 } else {
266 input.putback(ch);
267 correct = false;
268 }
269 } break;
270
271 }
272
273 if (!correct)
274 break;
275 }
276
277 if (p_state == p_e) {
278 // todo: check also some other error states
279 for (std::string::reverse_iterator r_it=
280 exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
281 input.putback(*r_it);
282 for (std::string::reverse_iterator r_it=
283 value_str.rbegin(); r_it != value_str.rend(); r_it++)
284 input.putback(*r_it);
285 return false;
286 }
287
288 if (value_str.size() > 0) {
289 std::istringstream(value_str) >> *value;
290 *value*=sign;
291
292 if (exp_value_str.size()) {
293 std::istringstream(exp_value_str) >> e_value;
294
295 if (e_value && e_sign==-1) {
296 // should have been catched by parse_float
297 for (std::string::reverse_iterator r_it=
298 exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
299 input.putback(*r_it);
300 for (std::string::reverse_iterator r_it=
301 value_str.rbegin(); r_it != value_str.rend(); r_it++)
302 input.putback(*r_it);
303
304 return false;
305 }
306 *value *= pow(10, e_value);
307 }
308
309 return true;
310 } else {
311 return false;
312 }
313 }
314
315
parse_float(std::istream & input,double * value)316 bool parse_float(std::istream& input, double* value) {
317 eat_whitespaces(input);
318 char ch;
319 std::string value_str;
320 std::string exp_str; // whole exp part
321 std::string exp_value_str; // value of exp part
322 int sign = 1;
323 int e_sign = 1;
324 bool correct = true;
325 int e_value;
326 bool has_dot = false;
327
328 enum {
329 p_number,
330 p_e,
331 p_enumber
332 } p_state = p_number;
333
334 if (match("-", input)) {
335 sign = -1;
336 } else {
337 match("+", input);
338 }
339
340 while(input && !input.eof()) {
341 input.get(ch);
342 bool end = false;
343 switch (p_state) {
344 case p_number: {
345 // DBG("st = p_number, ch=%c\n",ch);
346 if (ch == 'E' || ch == 'e') {
347 exp_str.push_back(ch);
348 p_state = p_e;
349 continue;
350 }
351 if (ch == '.') {
352 if (has_dot) {
353 correct = false;
354 break;
355 }
356 has_dot = true;
357 value_str.push_back(ch);
358 continue;
359 }
360
361 if (!isdigit(ch)) {
362 input.putback(ch);
363 end = true;
364 break;
365 }
366 value_str.push_back(ch);
367 } break;
368
369 case p_e: {
370 // DBG("st = p_e, ch=%c\n",ch);
371 if (ch == '+') {
372 exp_str.push_back(ch);
373 p_state = p_enumber;
374 } else if (ch == '-') {
375 e_sign = -1;
376 exp_str.push_back(ch);
377 p_state = p_enumber;
378 } else if (isdigit(ch)) {
379 exp_value_str.push_back(ch);
380 exp_str.push_back(ch);
381 p_state = p_enumber;
382 } else {
383 input.putback(ch);
384 correct = false;
385 }
386 } break;
387
388 case p_enumber: {
389 // DBG("st = p_enumber, ch=%c\n",ch);
390
391 if (isdigit(ch)) {
392 exp_value_str.push_back(ch);
393 exp_str.push_back(ch);
394 } else {
395 input.putback(ch);
396 end = true;
397 }
398 } break;
399
400 }
401
402 if (end || !correct)
403 break;
404 }
405
406 // DBG("correct = %s, has_dot = %s, e_sign = %d, exp_value_str.size() = %zd\n",
407 // correct?"true":"false",has_dot?"true":"false",e_sign,exp_value_str.size());
408 if (!correct || (!has_dot && !(e_sign == -1 && exp_value_str.size())) || p_state == p_e) {
409 // todo: check also some other error states
410 for (std::string::reverse_iterator r_it=
411 exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
412 input.putback(*r_it);
413 for (std::string::reverse_iterator r_it=
414 value_str.rbegin(); r_it != value_str.rend(); r_it++)
415 input.putback(*r_it);
416 return false;
417 }
418
419 if (value_str.size() > 0) {
420 std::istringstream(value_str) >> *value;
421 *value*=sign;
422
423 if (exp_value_str.size()) {
424 std::istringstream(exp_value_str) >> e_value;
425
426 *value *= pow(10, e_sign*e_value);
427 }
428
429 return true;
430 } else {
431 return false;
432 }
433 }
434
Object()435 Object::Object() : value_map_() {}
436
~Object()437 Object::~Object() {
438 std::map<std::string, Value*>::iterator i;
439 for (i = value_map_.begin(); i != value_map_.end(); ++i) {
440 delete i->second;
441 }
442 }
443
parse(std::istream & input)444 bool Object::parse(std::istream& input) {
445 if (!match("{", input)) {
446 return false;
447 }
448
449 do {
450 std::string key;
451 if (!parse_string(input, &key)) {
452 return false;
453 }
454 if (!match(":", input)) {
455 return false;
456 }
457 Value* v = new Value();
458 if (!v->parse(input)) {
459 delete v;
460 break;
461 }
462 value_map_[key] = v;
463 } while (match(",", input));
464
465 if (!match("}", input)) {
466 return false;
467 }
468 return true;
469 }
470
Value()471 Value::Value() : type_(INVALID_) {}
472
~Value()473 Value::~Value() {
474 if (type_ == STRING_) {
475 delete string_value_;
476 }
477 if (type_ == OBJECT_) {
478 delete object_value_;
479 }
480 if (type_ == ARRAY_) {
481 delete array_value_;
482 }
483 }
484
parse(std::istream & input)485 bool Value::parse(std::istream& input) {
486 std::string string_value;
487 if (parse_string(input, &string_value)) {
488 string_value_ = new std::string();
489 string_value_->swap(string_value);
490 type_ = STRING_;
491 return true;
492 }
493 if (parse_number(input, &integer_value_)) {
494 type_ = INTEGER_;
495 return true;
496 }
497
498 if (parse_bool(input, &bool_value_)) {
499 type_ = BOOL_;
500 return true;
501 }
502 if (parse_null(input)) {
503 type_ = NULL_;
504 return true;
505 }
506 array_value_ = new Array();
507 if (array_value_->parse(input)) {
508 type_ = ARRAY_;
509 return true;
510 }
511 delete array_value_;
512 object_value_ = new Object();
513 if (object_value_->parse(input)) {
514 type_ = OBJECT_;
515 return true;
516 }
517 delete object_value_;
518 return false;
519 }
520
Array()521 Array::Array() : values_() {}
522
~Array()523 Array::~Array() {
524 for (unsigned int i = 0; i < values_.size(); ++i) {
525 delete values_[i];
526 }
527 }
528
parse(std::istream & input)529 bool Array::parse(std::istream& input) {
530 if (!match("[", input)) {
531 return false;
532 }
533
534 do {
535 Value* v = new Value();
536 if (!v->parse(input)) {
537 delete v;
538 break;
539 }
540 values_.push_back(v);
541 } while (match(",", input));
542
543 if (!match("]", input)) {
544 return false;
545 }
546 return true;
547 }
548
549 } // namespace jsonxx
550