data.cc 24.3 KB
Newer Older
Jelte Jansen's avatar
Jelte Jansen committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
#include <config.h>
16

17
#include <cc/data.h>
18

19
#include <cstring>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
20
#include <cassert>
Jeremy C. Reed's avatar
Jeremy C. Reed committed
21
#include <climits>
22
#include <map>
23 24
#include <cstdio>
#include <iostream>
25
#include <string>
26
#include <sstream>
27
#include <cerrno>
28
#include <climits>
29

30
#include <boost/algorithm/string.hpp> // for iequals
31
#include <boost/lexical_cast.hpp>
32

Jelte Jansen's avatar
Jelte Jansen committed
33 34
#include <cmath>

35 36
using namespace std;

37
namespace {
38
const char* const WHITESPACE = " \b\f\n\r\t";
39 40
} // end anonymous namespace

JINMEI Tatuya's avatar
JINMEI Tatuya committed
41 42 43
namespace isc {
namespace data {

44
std::string
45
Element::str() const {
46 47
    std::stringstream ss;
    toJSON(ss);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
48
    return (ss.str());
49 50 51
}

std::string
52
Element::toWire() const {
53 54
    std::stringstream ss;
    toJSON(ss);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
55
    return (ss.str());
56 57 58
}

void
59
Element::toWire(std::ostream& ss) const {
60 61 62
    toJSON(ss);
}

63
bool
64
Element::getValue(int64_t&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
65
    return (false);
66 67 68
}

bool
69
Element::getValue(double&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
70
    return (false);
71 72 73
}

bool
74
Element::getValue(bool&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
75
    return (false);
76 77 78
}

bool
79
Element::getValue(std::string&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
80
    return (false);
81 82 83
}

bool
84
Element::getValue(std::vector<ConstElementPtr>&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
85
    return (false);
86 87 88
}

bool
89
Element::getValue(std::map<std::string, ConstElementPtr>&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
90
    return (false);
91 92 93
}

bool
94
Element::setValue(const long long int) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
95
    return (false);
96 97 98
}

bool
99
Element::setValue(const double) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
100
    return (false);
101 102 103
}

bool
104
Element::setValue(const bool) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
105
    return (false);
106 107 108
}

bool
109
Element::setValue(const std::string&) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
110
    return (false);
111 112 113
}

bool
114
Element::setValue(const std::vector<ConstElementPtr>&) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
115
    return (false);
116 117 118
}

bool
119
Element::setValue(const std::map<std::string, ConstElementPtr>&) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
120
    return (false);
121 122
}

123
ConstElementPtr
124
Element::get(const int) const {
125 126 127 128
    isc_throw(TypeError, "get(int) called on a non-list Element");
}

void
129
Element::set(const size_t, ConstElementPtr) {
130 131 132 133
    isc_throw(TypeError, "set(int, element) called on a non-list Element");
}

void
134
Element::add(ConstElementPtr) {
135 136 137 138
    isc_throw(TypeError, "add() called on a non-list Element");
}

void
139
Element::remove(const int) {
140 141 142 143
    isc_throw(TypeError, "remove(int) called on a non-list Element");
}

size_t
144
Element::size() const {
145 146 147
    isc_throw(TypeError, "size() called on a non-list Element");
}

148 149 150 151 152
bool
Element::empty() const {
    isc_throw(TypeError, "empty() called on a non-list Element");
}

153
ConstElementPtr
154
Element::get(const std::string&) const {
155 156 157 158
    isc_throw(TypeError, "get(string) called on a non-map Element");
}

void
159
Element::set(const std::string&, ConstElementPtr) {
160 161 162 163
    isc_throw(TypeError, "set(name, element) called on a non-map Element");
}

void
164
Element::remove(const std::string&) {
165 166 167 168
    isc_throw(TypeError, "remove(string) called on a non-map Element");
}

bool
169
Element::contains(const std::string&) const {
170 171 172
    isc_throw(TypeError, "contains(string) called on a non-map Element");
}

173
ConstElementPtr
174
Element::find(const std::string&) const {
175 176 177 178
    isc_throw(TypeError, "find(string) called on a non-map Element");
}

bool
179
Element::find(const std::string&, ConstElementPtr&) const {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
180
    return (false);
181 182
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
183 184
namespace {
inline void
185 186
throwJSONError(const std::string& error, const std::string& file, int line,
               int pos)
Jelte Jansen's avatar
Jelte Jansen committed
187
{
Jelte Jansen's avatar
Jelte Jansen committed
188 189 190
    std::stringstream ss;
    ss << error << " in " + file + ":" << line << ":" << pos;
    isc_throw(JSONError, ss.str());
191
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
192
}
193

194
std::ostream&
195
operator<<(std::ostream& out, const Element& e) {
196
    return (out << e.str());
197 198
}

199 200 201 202 203 204 205
bool
operator==(const Element& a, const Element& b) {
    return (a.equals(b));
}

bool operator!=(const Element& a, const Element& b) {
    return (!a.equals(b));
206 207
};

208 209 210
//
// factory functions
//
211 212
ElementPtr
Element::create() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
213
    return (ElementPtr(new NullElement()));
214 215
}

216
ElementPtr
217 218
Element::create(const long long int i) {
    return (ElementPtr(new IntElement(static_cast<int64_t>(i))));
219 220 221
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
222
Element::create(const double d) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
223
    return (ElementPtr(new DoubleElement(d)));
224 225 226
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
227
Element::create(const std::string& s) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
228
    return (ElementPtr(new StringElement(s)));
229 230 231
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
232
Element::create(const bool b) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
233
    return (ElementPtr(new BoolElement(b)));
234 235 236
}

ElementPtr
237
Element::createList() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
238
    return (ElementPtr(new ListElement()));
239 240 241
}

ElementPtr
242
Element::createMap() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
243
    return (ElementPtr(new MapElement()));
244 245 246 247
}


//
248
// helper functions for fromJSON factory
249
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
250 251
namespace {
bool
252
charIn(const int c, const char* chars) {
253 254
    const size_t chars_len = std::strlen(chars);
    for (size_t i = 0; i < chars_len; ++i) {
255
        if (chars[i] == c) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
256
            return (true);
257 258
        }
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
259
    return (false);
260 261
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
262
void
263
skipChars(std::istream& in, const char* chars, int& line, int& pos) {
264
    int c = in.peek();
265
    while (charIn(c, chars) && c != EOF) {
266
        if (c == '\n') {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
267
            ++line;
268 269
            pos = 1;
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
270
            ++pos;
271
        }
272
        in.ignore();
273 274 275 276 277 278
        c = in.peek();
    }
}

// skip on the input stream to one of the characters in chars
// if another character is found this function returns false
279
// unless that character is specified in the optional may_skip
280 281
//
// the character found is left on the stream
JINMEI Tatuya's avatar
JINMEI Tatuya committed
282
void
283 284
skipTo(std::istream& in, const std::string& file, int& line,
       int& pos, const char* chars, const char* may_skip="")
285
{
286
    int c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
287
    ++pos;
288
    while (c != EOF) {
289 290
        if (c == '\n') {
            pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
291
            ++line;
292
        }
293
        if (charIn(c, may_skip)) {
294
            c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
295
            ++pos;
296 297
        } else if (charIn(c, chars)) {
            while (charIn(in.peek(), may_skip)) {
298 299
                if (in.peek() == '\n') {
                    pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
300
                    ++line;
301
                }
302
                in.ignore();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
303
                ++pos;
304 305
            }
            in.putback(c);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
306
            --pos;
307
            return;
308
        } else {
309
            throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
310 311
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
312
    throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
313 314
}

315 316
// TODO: Should we check for all other official escapes here (and
// error on the rest)?
JINMEI Tatuya's avatar
JINMEI Tatuya committed
317
std::string
318 319
strFromStringstream(std::istream& in, const std::string& file,
                    const int line, int& pos) throw (JSONError)
320 321
{
    std::stringstream ss;
322
    int c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
323
    ++pos;
324 325
    if (c == '"') {
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
326
        ++pos;
327
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
328
        throwJSONError("String expected", file, line, pos);
329
    }
330

331
    while (c != EOF && c != '"') {
332 333
        if (c == '\\') {
            // see the spec for allowed escape characters
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
            switch (in.peek()) {
            case '"':
                c = '"';
                break;
            case '/':
                c = '/';
                break;
            case '\\':
                c = '\\';
                break;
            case 'b':
                c = '\b';
                break;
            case 'f':
                c = '\f';
                break;
            case 'n':
                c = '\n';
                break;
            case 'r':
                c = '\r';
                break;
            case 't':
                c = '\t';
                break;
            default:
360
                throwJSONError("Bad escape", file, line, pos);
361
            }
362
            // drop the escaped char
363
            in.ignore();
364
            ++pos;
365
        }
366
        ss.put(c);
367
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
368
        ++pos;
369
    }
370 371 372
    if (c == EOF) {
        throwJSONError("Unterminated string", file, line, pos);
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
373
    return (ss.str());
374 375
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
376
std::string
377
wordFromStringstream(std::istream& in, int& pos) {
378 379 380 381
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
382
    pos += ss.str().size();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
383
    return (ss.str());
384 385
}

386 387
std::string
numberFromStringstream(std::istream& in, int& pos) {
Jelte Jansen's avatar
Jelte Jansen committed
388 389 390 391
    std::stringstream ss;
    while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
           in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
        ss << (char) in.get();
392
    }
Jelte Jansen's avatar
Jelte Jansen committed
393
    pos += ss.str().size();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
394
    return (ss.str());
395 396
}

Jelte Jansen's avatar
Jelte Jansen committed
397 398 399
// Should we change from IntElement and DoubleElement to NumberElement
// that can also hold an e value? (and have specific getters if the
// value is larger than an int can handle)
400
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
401
ElementPtr
402 403
fromStringstreamNumber(std::istream& in, int& pos) {
    std::string number = numberFromStringstream(in, pos);
Jelte Jansen's avatar
Jelte Jansen committed
404

405 406 407 408 409
    if (number.find_first_of(".eE") < number.size()) {
        try {
            return (Element::create(boost::lexical_cast<double>(number)));
        } catch (const boost::bad_lexical_cast&) {
            isc_throw(JSONError, std::string("Number overflow: ") + number);
410
        }
Jelte Jansen's avatar
Jelte Jansen committed
411
    } else {
412 413 414
        try {
            return (Element::create(boost::lexical_cast<int64_t>(number)));
        } catch (const boost::bad_lexical_cast&) {
Jelte Jansen's avatar
Jelte Jansen committed
415 416
            isc_throw(JSONError, std::string("Number overflow: ") + number);
        }
417
    }
418 419
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
420
ElementPtr
421 422
fromStringstreamBool(std::istream& in, const std::string& file,
                     const int line, int& pos)
423
{
424
    const std::string word = wordFromStringstream(in, pos);
425
    if (boost::iequals(word, "True")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
426
        return (Element::create(true));
427
    } else if (boost::iequals(word, "False")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
428
        return (Element::create(false));
429
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
430
        throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
Jelte Jansen's avatar
Jelte Jansen committed
431
        // above is a throw shortcurt, return empty is never reached
JINMEI Tatuya's avatar
JINMEI Tatuya committed
432
        return (ElementPtr());
433 434 435
    }
}

436
ElementPtr
437 438
fromStringstreamNull(std::istream& in, const std::string& file,
                     const int line, int& pos)
439
{
440
    const std::string word = wordFromStringstream(in, pos);
441
    if (boost::iequals(word, "null")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
442
        return (Element::create());
443
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
444
        throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
445
        return (ElementPtr());
446 447 448
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
449
ElementPtr
450 451
fromStringstreamString(std::istream& in, const std::string& file, int& line,
                       int& pos)
452
{
453
    return (Element::create(strFromStringstream(in, file, line, pos)));
454 455
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
456
ElementPtr
457 458
fromStringstreamList(std::istream& in, const std::string& file, int& line,
                     int& pos)
459
{
460
    int c = 0;
461
    ElementPtr list = Element::createList();
462
    ConstElementPtr cur_list_element;
463

464
    skipChars(in, WHITESPACE, line, pos);
465
    while (c != EOF && c != ']') {
466
        if (in.peek() != ']') {
467
            cur_list_element = Element::fromJSON(in, file, line, pos);
468
            list->add(cur_list_element);
469
            skipTo(in, file, line, pos, ",]", WHITESPACE);
470 471
        }
        c = in.get();
472
        pos++;
473
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
474
    return (list);
475 476
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
477
ElementPtr
478 479
fromStringstreamMap(std::istream& in, const std::string& file, int& line,
                    int& pos)
480
{
481
    ElementPtr map = Element::createMap();
482
    skipChars(in, WHITESPACE, line, pos);
483
    int c = in.peek();
484 485
    if (c == EOF) {
        throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
Jelte Jansen's avatar
Jelte Jansen committed
486
    } else if (c == '}') {
487
        // empty map, skip closing curly
488
        in.ignore();
489 490
    } else {
        while (c != EOF && c != '}') {
491
            std::string key = strFromStringstream(in, file, line, pos);
492

493
            skipTo(in, file, line, pos, ":", WHITESPACE);
494
            // skip the :
495
            in.ignore();
496
            pos++;
497

498
            ConstElementPtr value = Element::fromJSON(in, file, line, pos);
499
            map->set(key, value);
500

501
            skipTo(in, file, line, pos, ",}", WHITESPACE);
502 503 504
            c = in.get();
            pos++;
        }
505
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
506
    return (map);
507
}
508
} // unnamed namespace
509

510
std::string
511
Element::typeToName(Element::types type) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
512
    switch (type) {
513
    case Element::integer:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
514
        return (std::string("integer"));
515
    case Element::real:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
516
        return (std::string("real"));
517
    case Element::boolean:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
518
        return (std::string("boolean"));
519
    case Element::string:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
520
        return (std::string("string"));
521
    case Element::list:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
522
        return (std::string("list"));
523
    case Element::map:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
524
        return (std::string("map"));
525
    case Element::null:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
526
        return (std::string("null"));
527
    case Element::any:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
528
        return (std::string("any"));
529
    default:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
530
        return (std::string("unknown"));
531 532 533 534 535 536
    }
}

Element::types
Element::nameToType(const std::string& type_name) {
    if (type_name == "integer") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
537
        return (Element::integer);
538
    } else if (type_name == "real") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
539
        return (Element::real);
540
    } else if (type_name == "boolean") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
541
        return (Element::boolean);
542
    } else if (type_name == "string") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
543
        return (Element::string);
544
    } else if (type_name == "list") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
545
        return (Element::list);
546
    } else if (type_name == "map") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
547
        return (Element::map);
548
    } else if (type_name == "named_set") {
549
        return (Element::map);
550
    } else if (type_name == "null") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
551
        return (Element::null);
552
    } else if (type_name == "any") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
553
        return (Element::any);
554 555 556 557 558
    } else {
        isc_throw(TypeError, type_name + " is not a valid type name");
    }
}

559
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
560
Element::fromJSON(std::istream& in) throw(JSONError) {
561
    int line = 1, pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
562
    return (fromJSON(in, "<istream>", line, pos));
563 564 565
}

ElementPtr
566 567
Element::fromJSON(std::istream& in, const std::string& file_name)
    throw(JSONError)
568 569
{
    int line = 1, pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
570
    return (fromJSON(in, file_name, line, pos));
571 572 573
}

ElementPtr
574
Element::fromJSON(std::istream& in, const std::string& file, int& line,
575
                  int& pos) throw(JSONError)
576
{
577
    int c = 0;
578 579
    ElementPtr element;
    bool el_read = false;
580
    skipChars(in, WHITESPACE, line, pos);
581 582
    while (c != EOF && !el_read) {
        c = in.get();
583
        pos++;
584 585 586 587 588 589 590 591 592 593 594
        switch(c) {
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
595 596
            case '-':
            case '+':
Jelte Jansen's avatar
Jelte Jansen committed
597 598
            case '.':
                in.putback(c);
599
                element = fromStringstreamNumber(in, pos);
600 601
                el_read = true;
                break;
602 603 604 605 606
            case 't':
            case 'T':
            case 'f':
            case 'F':
                in.putback(c);
607
                element = fromStringstreamBool(in, file, line, pos);
608 609
                el_read = true;
                break;
610
            case 'n':
611
            case 'N':
612
                in.putback(c);
613
                element = fromStringstreamNull(in, file, line, pos);
614 615
                el_read = true;
                break;
616 617
            case '"':
                in.putback('"');
618
                element = fromStringstreamString(in, file, line, pos);
619 620 621
                el_read = true;
                break;
            case '[':
622
                element = fromStringstreamList(in, file, line, pos);
623 624 625
                el_read = true;
                break;
            case '{':
626
                element = fromStringstreamMap(in, file, line, pos);
627 628
                el_read = true;
                break;
629 630
            case EOF:
                break;
631
            default:
632
                throwJSONError(std::string("error: unexpected character ") + std::string(1, c), file, line, pos);
633 634 635 636
                break;
        }
    }
    if (el_read) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
637
        return (element);
638
    } else {
639
        isc_throw(JSONError, "nothing read");
640 641 642
    }
}

643
ElementPtr
644
Element::fromJSON(const std::string& in) {
645 646
    std::stringstream ss;
    ss << in;
647 648
    int line = 1, pos = 1;
    ElementPtr result(fromJSON(ss, "<string>", line, pos));
649
    skipChars(ss, WHITESPACE, line, pos);
650 651 652 653 654
    // ss must now be at end
    if (ss.peek() != EOF) {
        throwJSONError("Extra data", "<string>", line, pos);
    }
    return result;
655
}
656

657 658 659
// to JSON format

void
660
IntElement::toJSON(std::ostream& ss) const {
661
    ss << intValue();
662 663
}

664
void
665
DoubleElement::toJSON(std::ostream& ss) const {
666
    ss << doubleValue();
667 668
}

669
void
670
BoolElement::toJSON(std::ostream& ss) const {
671
    if (boolValue()) {
672
        ss << "true";
673
    } else {
674
        ss << "false";
675 676 677
    }
}

678
void
679
NullElement::toJSON(std::ostream& ss) const {
680 681 682 683
    ss << "null";
}

void
684
StringElement::toJSON(std::ostream& ss) const {
685
    ss << "\"";
686 687
    const std::string& str = stringValue();
    for (size_t i = 0; i < str.size(); ++i) {
Mukund Sivaraman's avatar
Mukund Sivaraman committed
688
        const char c = str[i];
689
        // Escape characters as defined in JSON spec
690 691
        // Note that we do not escape forward slash; this
        // is allowed, but not mandatory.
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
        switch (c) {
        case '"':
            ss << '\\' << c;
            break;
        case '\\':
            ss << '\\' << c;
            break;
        case '\b':
            ss << '\\' << 'b';
            break;
        case '\f':
            ss << '\\' << 'f';
            break;
        case '\n':
            ss << '\\' << 'n';
            break;
        case '\r':
            ss << '\\' << 'r';
            break;
        case '\t':
            ss << '\\' << 't';
            break;
        default:
            ss << c;
716 717
        }
    }
718 719 720
    ss << "\"";
}

721
void
722
ListElement::toJSON(std::ostream& ss) const {