1/*!
2 * Copyright (c) 2015 by Contributors
3 * \file json.h
4 * \brief Lightweight JSON Reader/Writer that read save into C++ data structs.
5 * This includes STL composites and structures.
6 */
7#ifndef DMLC_JSON_H_
8#define DMLC_JSON_H_
9
10// This code requires C++11 to compile
11#include <vector>
12#ifndef _LIBCPP_SGX_NO_IOSTREAMS
13#include <iostream>
14#include <sstream>
15#endif
16#include <cctype>
17#include <string>
18#include <algorithm>
19#include <map>
20#include <list>
21#include <utility>
22
23#include "./base.h"
24#include "./logging.h"
25#include "./type_traits.h"
26
27#if DMLC_USE_CXX11
28#include <typeindex>
29#include <typeinfo>
30#include <unordered_map>
31#if DMLC_STRICT_CXX11
32#if DMLC_ENABLE_RTTI
33#include "./any.h"
34#endif // DMLC_ENABLE_RTTI
35#endif // DMLC_STRICT_CXX11
36#endif // DMLC_USE_CXX11
37
38namespace dmlc {
39/*!
40 * \brief Lightweight JSON Reader to read any STL compositions and structs.
41 * The user need to know the schema of the
42 *
43 */
44class JSONReader {
45 public:
46 /*!
47 * \brief Constructor.
48 * \param is the input source.
49 */
50#ifndef _LIBCPP_SGX_NO_IOSTREAMS
51 explicit JSONReader(std::istream *is)
52#else
53 explicit JSONReader(std::string *is)
54#endif
55 : is_(is),
56 line_count_r_(0),
57 line_count_n_(0) {}
58 /*!
59 * \brief Parse next JSON string.
60 * \param out_str the output string.
61 * \throw dmlc::Error when next token is not string
62 */
63 inline void ReadString(std::string *out_str);
64 /*!
65 * \brief Read Number.
66 * \param out_value output value;
67 * \throw dmlc::Error when next token is not number of ValueType.
68 * \tparam ValueType type of the number
69 */
70 template<typename ValueType>
71 inline void ReadNumber(ValueType *out_value);
72 /*!
73 * \brief Begin parsing an object.
74 * \code
75 * std::string key;
76 * // value can be any type that is json serializable.
77 * std::string value;
78 * reader->BeginObject();
79 * while (reader->NextObjectItem(&key)) {
80 * // do somthing to key value
81 * reader->Read(&value);
82 * }
83 * \endcode
84 */
85 inline void BeginObject();
86 /*!
87 * \brief Begin parsing an array.
88 * \code
89 * // value can be any type that is json serializable.
90 * std::string value;
91 * reader->BeginArray();
92 * while (reader->NextArrayItem()) {
93 * reader->Read(&value);
94 * // do somthing to value
95 * }
96 * \endcode
97 */
98 inline void BeginArray();
99 /*!
100 * \brief Try to move to next object item.
101 * If this call is successful, user can proceed to call
102 * reader->Read to read in the value.
103 * \param out_key the key to the next object.
104 * \return true if the read is successful, false if we are at end of the object.
105 */
106 inline bool NextObjectItem(std::string *out_key);
107 /*!
108 * \brief Try to read the next element in the array.
109 * If this call is successful, user can proceed to call
110 * reader->Read to read in the value.
111 * \return true if the read is successful, false if we are at end of the array.
112 */
113 inline bool NextArrayItem();
114 /*!
115 * \brief Read next ValueType.
116 * \param out_value any STL or json readable type to be read
117 * \throw dmlc::Error when the read of ValueType is not successful.
118 * \tparam ValueType the data type to be read.
119 */
120 template<typename ValueType>
121 inline void Read(ValueType *out_value);
122
123 /*! \return current line count */
124 inline std::string line_info() const {
125#ifndef _LIBCPP_SGX_NO_IOSTREAMS
126 char temp[64];
127 std::ostringstream os;
128 os << " Line " << std::max(line_count_r_, line_count_n_);
129 is_->getline(temp, 64);
130 os << ", around ^`" << temp << "`";
131 return os.str();
132#else
133 std::string info = " Line ";
134 info += std::to_string(std::max(line_count_r_, line_count_n_));
135
136 // string getline
137 size_t end_pos = is_->find('\n');
138 end_pos = std::min(static_cast<size_t>(64),
139 end_pos == std::string::npos ? is_->size() : end_pos);
140 std::string line = is_->substr(0, end_pos);
141 is_->erase(0, line.size() + 1); // +1 for \n
142
143 info += ", around ^`" + line + "`";
144 return info;
145#endif
146 }
147
148 private:
149#ifndef _LIBCPP_SGX_NO_IOSTREAMS
150 /*! \brief internal reader stream */
151 std::istream *is_;
152#else
153 /*! \brief internal reader string */
154 std::string *is_;
155#endif
156 /*! \brief "\\r" counter */
157 size_t line_count_r_;
158 /*! \brief "\\n" counter */
159 size_t line_count_n_;
160 /*!
161 * \brief record how many element processed in
162 * current array/object scope.
163 */
164 std::vector<size_t> scope_counter_;
165 /*!
166 * \brief Read next nonspace character.
167 * \return the next nonspace character.
168 */
169 inline int NextNonSpace();
170 /*!
171 * \brief Read just before next nonspace but not read that.
172 * \return the next nonspace character.
173 */
174 inline int PeekNextNonSpace();
175 /*!
176 * \brief Takes the next char from the input source.
177 * \return the next character.
178 */
179 inline int NextChar();
180 /*!
181 * \brief Returns the next char from the input source.
182 * \return the next character.
183 */
184 inline int PeekNextChar();
185};
186
187/*!
188 * \brief Lightweight json to write any STL compositions.
189 */
190class JSONWriter {
191 public:
192 /*!
193 * \brief Constructor.
194 * \param os the output reciever.
195 */
196#ifndef _LIBCPP_SGX_NO_IOSTREAMS
197 explicit JSONWriter(std::ostream *os)
198#else
199 explicit JSONWriter(std::string *os)
200#endif
201 : os_(os) {}
202 /*!
203 * \brief Write a string that do not contain escape characters.
204 * \param s the string to be written.
205 */
206 inline void WriteNoEscape(const std::string &s);
207 /*!
208 * \brief Write a string that can contain escape characters.
209 * \param s the string to be written.
210 */
211 inline void WriteString(const std::string &s);
212 /*!
213 * \brief Write a string that can contain escape characters.
214 * \param v the value to be written.
215 * \tparam ValueType The value type to be written.
216 */
217 template<typename ValueType>
218 inline void WriteNumber(const ValueType &v);
219 /*!
220 * \brief Start beginning of array.
221 * \param multi_line whether to start an multi_line array.
222 * \code
223 * writer->BeginArray();
224 * for (auto& v : vdata) {
225 * writer->WriteArrayItem(v);
226 * }
227 * writer->EndArray();
228 * \endcode
229 */
230 inline void BeginArray(bool multi_line = true);
231 /*! \brief Finish writing an array. */
232 inline void EndArray();
233 /*!
234 * \brief Start beginning of array.
235 * \param multi_line whether to start an multi_line array.
236 * \code
237 * writer->BeginObject();
238 * for (auto& kv : vmap) {
239 * writer->WriteObjectKeyValue(kv.first, kv.second);
240 * }
241 * writer->EndObject();
242 * \endcode
243 */
244 inline void BeginObject(bool multi_line = true);
245 /*! \brief Finish writing object. */
246 inline void EndObject();
247 /*!
248 * \brief Write key value pair in the object.
249 * \param key the key of the object.
250 * \param value the value of to be written.
251 * \tparam ValueType The value type to be written.
252 */
253 template<typename ValueType>
254 inline void WriteObjectKeyValue(const std::string &key,
255 const ValueType &value);
256 /*!
257 * \brief Write seperator of array, before writing next element.
258 * User can proceed to call writer->Write to write next item
259 */
260 inline void WriteArraySeperator();
261 /*!
262 * \brief Write value into array.
263 * \param value The value of to be written.
264 * \tparam ValueType The value type to be written.
265 */
266 template<typename ValueType>
267 inline void WriteArrayItem(const ValueType &value);
268 /*!
269 * \brief Write value to json.
270 * \param value any STL or json readable that can be written.
271 * \tparam ValueType the data type to be write.
272 */
273 template<typename ValueType>
274 inline void Write(const ValueType &value);
275
276 private:
277#ifndef _LIBCPP_SGX_NO_IOSTREAMS
278 /*! \brief Output stream */
279 std::ostream *os_;
280#else
281 std::string *os_;
282#endif
283 /*!
284 * \brief record how many element processed in
285 * current array/object scope.
286 */
287 std::vector<size_t> scope_counter_;
288 /*! \brief Record whether current is a multiline scope */
289 std::vector<bool> scope_multi_line_;
290 /*!
291 * \brief Write seperating space and newlines
292 */
293 inline void WriteSeperator();
294};
295
296/*!
297 * \brief Helper class to read JSON into a class or struct object.
298 * \code
299 * struct Param {
300 * std::string name;
301 * int value;
302 * // define load function from JSON
303 * inline void Load(dmlc::JSONReader *reader) {
304 * dmlc::JSONStructReadHelper helper;
305 * helper.DeclareField("name", &name);
306 * helper.DeclareField("value", &value);
307 * helper.ReadAllFields(reader);
308 * }
309 * };
310 * \endcode
311 */
312class JSONObjectReadHelper {
313 public:
314 /*!
315 * \brief Declare field of type T
316 * \param key the key of the of field.
317 * \param addr address of the data type.
318 * \tparam T the data type to be read, must be STL composition of JSON serializable.
319 */
320 template<typename T>
321 inline void DeclareField(const std::string &key, T *addr) {
322 DeclareFieldInternal(key, addr, false);
323 }
324 /*!
325 * \brief Declare optional field of type T
326 * \param key the key of the of field.
327 * \param addr address of the data type.
328 * \tparam T the data type to be read, must be STL composition of JSON serializable.
329 */
330 template<typename T>
331 inline void DeclareOptionalField(const std::string &key, T *addr) {
332 DeclareFieldInternal(key, addr, true);
333 }
334 /*!
335 * \brief Read in all the declared fields.
336 * \param reader the JSONReader to read the json.
337 */
338 inline void ReadAllFields(JSONReader *reader);
339
340 private:
341 /*!
342 * \brief Internal function to declare field.
343 * \param key the key of the of field.
344 * \param addr address of the data type.
345 * \param optional if set to true, no error will be reported if the key is not presented.
346 * \tparam T the data type to be read, must be STL composition of JSON serializable.
347 */
348 template<typename T>
349 inline void DeclareFieldInternal(const std::string &key, T *addr, bool optional);
350 /*!
351 * \brief The internal reader function.
352 * \param reader The reader to read.
353 * \param addr The memory address to read.
354 */
355 template<typename T>
356 inline static void ReaderFunction(JSONReader *reader, void *addr);
357 /*! \brief callback type to reader function */
358 typedef void (*ReadFunction)(JSONReader *reader, void *addr);
359 /*! \brief internal data entry */
360 struct Entry {
361 /*! \brief the reader function */
362 ReadFunction func;
363 /*! \brief the address to read */
364 void *addr;
365 /*! \brief whether it is optional */
366 bool optional;
367 };
368 /*! \brief the internal map of reader callbacks */
369 std::map<std::string, Entry> map_;
370};
371
372#define DMLC_JSON_ENABLE_ANY_VAR_DEF(KeyName) \
373 static DMLC_ATTRIBUTE_UNUSED ::dmlc::json::AnyJSONManager& \
374 __make_AnyJSONType ## _ ## KeyName ## __
375
376/*!
377 * \def DMLC_JSON_ENABLE_ANY
378 * \brief Macro to enable save/load JSON of dmlc:: whose actual type is Type.
379 * Any type will be saved as json array [KeyName, content]
380 *
381 * \param Type The type to be registered.
382 * \param KeyName The Type key assigned to the type, must be same during load.
383 */
384#define DMLC_JSON_ENABLE_ANY(Type, KeyName) \
385 DMLC_STR_CONCAT(DMLC_JSON_ENABLE_ANY_VAR_DEF(KeyName), __COUNTER__) = \
386 ::dmlc::json::AnyJSONManager::Global()->EnableType<Type>(#KeyName) \
387
388//! \cond Doxygen_Suppress
389namespace json {
390
391/*!
392 * \brief generic serialization handler
393 * \tparam T the type to be serialized
394 */
395template<typename T>
396struct Handler;
397
398template<typename ValueType>
399struct NumericHandler {
400 inline static void Write(JSONWriter *writer, const ValueType &value) {
401 writer->WriteNumber<ValueType>(value);
402 }
403 inline static void Read(JSONReader *reader, ValueType *value) {
404 reader->ReadNumber<ValueType>(value);
405 }
406};
407
408template<typename ContainerType>
409struct ArrayHandler {
410 inline static void Write(JSONWriter *writer, const ContainerType &array) {
411 typedef typename ContainerType::value_type ElemType;
412 writer->BeginArray(array.size() > 10 || !dmlc::is_pod<ElemType>::value);
413 for (typename ContainerType::const_iterator it = array.begin();
414 it != array.end(); ++it) {
415 writer->WriteArrayItem(*it);
416 }
417 writer->EndArray();
418 }
419 inline static void Read(JSONReader *reader, ContainerType *array) {
420 typedef typename ContainerType::value_type ElemType;
421 array->clear();
422 reader->BeginArray();
423 while (reader->NextArrayItem()) {
424 ElemType value;
425 Handler<ElemType>::Read(reader, &value);
426 array->insert(array->end(), value);
427 }
428 }
429};
430
431template<typename ContainerType>
432struct MapHandler{
433 inline static void Write(JSONWriter *writer, const ContainerType &map) {
434 writer->BeginObject(map.size() > 1);
435 for (typename ContainerType::const_iterator it = map.begin(); it != map.end(); ++it) {
436 writer->WriteObjectKeyValue(it->first, it->second);
437 }
438 writer->EndObject();
439 }
440 inline static void Read(JSONReader *reader, ContainerType *map) {
441 typedef typename ContainerType::mapped_type ElemType;
442 map->clear();
443 reader->BeginObject();
444 std::string key;
445 while (reader->NextObjectItem(&key)) {
446 ElemType value;
447 reader->Read(&value);
448 (*map)[key] = value;
449 }
450 }
451};
452
453template<typename T>
454struct CommonJSONSerializer {
455 inline static void Write(JSONWriter *writer, const T &value) {
456 value.Save(writer);
457 }
458 inline static void Read(JSONReader *reader, T *value) {
459 value->Load(reader);
460 }
461};
462
463template<>
464struct Handler<std::string> {
465 inline static void Write(JSONWriter *writer, const std::string &value) {
466 writer->WriteString(value);
467 }
468 inline static void Read(JSONReader *reader, std::string *str) {
469 reader->ReadString(str);
470 }
471};
472
473template<typename T>
474struct Handler<std::vector<T> > : public ArrayHandler<std::vector<T> > {
475};
476
477template<typename K, typename V>
478struct Handler<std::pair<K, V> > {
479 inline static void Write(JSONWriter *writer, const std::pair<K, V> &kv) {
480 writer->BeginArray();
481 writer->WriteArrayItem(kv.first);
482 writer->WriteArrayItem(kv.second);
483 writer->EndArray();
484 }
485 inline static void Read(JSONReader *reader, std::pair<K, V> *kv) {
486 reader->BeginArray();
487 CHECK(reader->NextArrayItem())
488 << "Expect array of length 2";
489 Handler<K>::Read(reader, &(kv->first));
490 CHECK(reader->NextArrayItem())
491 << "Expect array of length 2";
492 Handler<V>::Read(reader, &(kv->second));
493 CHECK(!reader->NextArrayItem())
494 << "Expect array of length 2";
495 }
496};
497
498template<typename T>
499struct Handler<std::list<T> > : public ArrayHandler<std::list<T> > {
500};
501
502template<typename V>
503struct Handler<std::map<std::string, V> > : public MapHandler<std::map<std::string, V> > {
504};
505
506#if DMLC_USE_CXX11
507template<typename V>
508struct Handler<std::unordered_map<std::string, V> >
509 : public MapHandler<std::unordered_map<std::string, V> > {
510};
511#endif // DMLC_USE_CXX11
512
513template<typename T>
514struct Handler {
515 inline static void Write(JSONWriter *writer, const T &data) {
516 typedef typename dmlc::IfThenElseType<dmlc::is_arithmetic<T>::value,
517 NumericHandler<T>,
518 CommonJSONSerializer<T> >::Type THandler;
519 THandler::Write(writer, data);
520 }
521 inline static void Read(JSONReader *reader, T *data) {
522 typedef typename dmlc::IfThenElseType<dmlc::is_arithmetic<T>::value,
523 NumericHandler<T>,
524 CommonJSONSerializer<T> >::Type THandler;
525 THandler::Read(reader, data);
526 }
527};
528
529#if DMLC_STRICT_CXX11
530#if DMLC_ENABLE_RTTI
531// Manager to store json serialization strategy.
532class AnyJSONManager {
533 public:
534 template<typename T>
535 inline AnyJSONManager& EnableType(const std::string& type_name) { // NOLINT(*)
536 std::type_index tp = std::type_index(typeid(T));
537 if (type_name_.count(tp) != 0) {
538 CHECK(type_name_.at(tp) == type_name)
539 << "Type has already been registered as another typename " << type_name_.at(tp);
540 return *this;
541 }
542 CHECK(type_map_.count(type_name) == 0)
543 << "Type name " << type_name << " already registered in registry";
544 Entry e;
545 e.read = ReadAny<T>;
546 e.write = WriteAny<T>;
547 type_name_[tp] = type_name;
548 type_map_[type_name] = e;
549 return *this;
550 }
551 // return global singleton
552 inline static AnyJSONManager* Global() {
553 static AnyJSONManager inst;
554 return &inst;
555 }
556
557 private:
558 AnyJSONManager() {}
559
560 template<typename T>
561 inline static void WriteAny(JSONWriter *writer, const any &data) {
562 writer->Write(dmlc::unsafe_get<T>(data));
563 }
564 template<typename T>
565 inline static void ReadAny(JSONReader *reader, any* data) {
566 T temp;
567 reader->Read(&temp);
568 *data = std::move(temp);
569 }
570 // data entry to store vtable for any type
571 struct Entry {
572 void (*read)(JSONReader* reader, any *data);
573 void (*write)(JSONWriter* reader, const any& data);
574 };
575
576 template<typename T>
577 friend struct Handler;
578
579 std::unordered_map<std::type_index, std::string> type_name_;
580 std::unordered_map<std::string, Entry> type_map_;
581};
582
583template<>
584struct Handler<any> {
585 inline static void Write(JSONWriter *writer, const any &data) {
586 std::unordered_map<std::type_index, std::string>&
587 nmap = AnyJSONManager::Global()->type_name_;
588 std::type_index id = std::type_index(data.type());
589 auto it = nmap.find(id);
590 CHECK(it != nmap.end() && it->first == id)
591 << "Type " << id.name() << " has not been registered via DMLC_JSON_ENABLE_ANY";
592 std::string type_name = it->second;
593 AnyJSONManager::Entry e = AnyJSONManager::Global()->type_map_.at(type_name);
594 writer->BeginArray(false);
595 writer->WriteArrayItem(type_name);
596 writer->WriteArraySeperator();
597 e.write(writer, data);
598 writer->EndArray();
599 }
600 inline static void Read(JSONReader *reader, any *data) {
601 std::string type_name;
602 reader->BeginArray();
603 CHECK(reader->NextArrayItem()) << "invalid any json format";
604 Handler<std::string>::Read(reader, &type_name);
605 std::unordered_map<std::string, AnyJSONManager::Entry>&
606 tmap = AnyJSONManager::Global()->type_map_;
607 auto it = tmap.find(type_name);
608 CHECK(it != tmap.end() && it->first == type_name)
609 << "Typename " << type_name << " has not been registered via DMLC_JSON_ENABLE_ANY";
610 AnyJSONManager::Entry e = it->second;
611 CHECK(reader->NextArrayItem()) << "invalid any json format";
612 e.read(reader, data);
613 CHECK(!reader->NextArrayItem()) << "invalid any json format";
614 }
615};
616#endif // DMLC_ENABLE_RTTI
617#endif // DMLC_STRICT_CXX11
618
619} // namespace json
620
621// implementations of JSONReader/Writer
622inline int JSONReader::NextChar() {
623#ifndef _LIBCPP_SGX_NO_IOSTREAMS
624 return is_->get();
625#else
626 int ch = is_->at(0);
627 is_->erase(0, 1);
628 return ch;
629#endif
630}
631
632inline int JSONReader::PeekNextChar() {
633#ifndef _LIBCPP_SGX_NO_IOSTREAMS
634 return is_->peek();
635#else
636 return is_->at(0);
637#endif
638}
639
640inline int JSONReader::NextNonSpace() {
641 int ch;
642 do {
643 ch = NextChar();
644 if (ch == '\n') ++line_count_n_;
645 if (ch == '\r') ++line_count_r_;
646 } while (isspace(ch));
647 return ch;
648}
649
650inline int JSONReader::PeekNextNonSpace() {
651 int ch;
652 while (true) {
653 ch = PeekNextChar();
654 if (ch == '\n') ++line_count_n_;
655 if (ch == '\r') ++line_count_r_;
656 if (!isspace(ch)) break;
657 NextChar();
658 }
659 return ch;
660}
661
662namespace {
663 template<typename T>
664#ifndef _LIBCPP_SGX_NO_IOSTREAMS
665 void Extend(std::ostream *os, T item) {
666 *os << item;
667 }
668#else
669 void Extend(std::string *ostr, T item) {
670 *ostr += item;
671 }
672#endif
673} // namespace
674
675inline void JSONReader::ReadString(std::string *out_str) {
676 int ch = NextNonSpace();
677 CHECK_EQ(ch, '\"')
678 << "Error at" << line_info()
679 << ", Expect \'\"\' but get \'" << static_cast<char>(ch) << '\'';
680#ifndef _LIBCPP_SGX_NO_IOSTREAMS
681 std::ostringstream output;
682#else
683 std::string output = "";
684#endif
685 while (true) {
686 ch = NextChar();
687 if (ch == '\\') {
688 char sch = static_cast<char>(NextChar());
689 switch (sch) {
690 case 'r': Extend(&output, "\r"); break;
691 case 'n': Extend(&output, "\n"); break;
692 case '\\': Extend(&output, "\\"); break;
693 case 't': Extend(&output, "\t"); break;
694 case '\"': Extend(&output, "\""); break;
695 default: LOG(FATAL) << "unknown string escape \\" << sch;
696 }
697 } else {
698 if (ch == '\"') break;
699 Extend(&output, static_cast<char>(ch));
700 }
701 if (ch == EOF || ch == '\r' || ch == '\n') {
702 LOG(FATAL)
703 << "Error at" << line_info()
704 << ", Expect \'\"\' but reach end of line ";
705 }
706 }
707#ifndef _LIBCPP_SGX_NO_IOSTREAMS
708 *out_str = output.str();
709#else
710 *out_str = output;
711#endif
712}
713
714template<typename ValueType>
715inline void JSONReader::ReadNumber(ValueType *out_value) {
716#ifndef _LIBCPP_SGX_NO_IOSTREAMS
717 *is_ >> *out_value;
718 CHECK(!is_->fail())
719 << "Error at" << line_info()
720 << ", Expect number";
721#else
722 char* endptr;
723 const char* icstr = is_->c_str();
724 unsigned number = strtol(icstr, &endptr, 10);
725 is_->erase(0, endptr - icstr);
726 *out_value = static_cast<ValueType>(number);
727#endif
728}
729
730inline void JSONReader::BeginObject() {
731 int ch = NextNonSpace();
732 CHECK_EQ(ch, '{')
733 << "Error at" << line_info()
734 << ", Expect \'{\' but get \'" << static_cast<char>(ch) << '\'';
735 scope_counter_.push_back(0);
736}
737
738inline void JSONReader::BeginArray() {
739 int ch = NextNonSpace();
740 CHECK_EQ(ch, '[')
741 << "Error at" << line_info()
742 << ", Expect \'[\' but get \'" << static_cast<char>(ch) << '\'';
743 scope_counter_.push_back(0);
744}
745
746inline bool JSONReader::NextObjectItem(std::string *out_key) {
747 bool next = true;
748 if (scope_counter_.back() != 0) {
749 int ch = NextNonSpace();
750 if (ch == EOF) {
751 next = false;
752 } else if (ch == '}') {
753 next = false;
754 } else {
755 CHECK_EQ(ch, ',')
756 << "Error at" << line_info()
757 << ", JSON object expect \'}\' or \',\' \'" << static_cast<char>(ch) << '\'';
758 }
759 } else {
760 int ch = PeekNextNonSpace();
761 if (ch == '}') {
762 NextChar();
763 next = false;
764 }
765 }
766 if (!next) {
767 scope_counter_.pop_back();
768 return false;
769 } else {
770 scope_counter_.back() += 1;
771 ReadString(out_key);
772 int ch = NextNonSpace();
773 CHECK_EQ(ch, ':')
774 << "Error at" << line_info()
775 << ", Expect \':\' but get \'" << static_cast<char>(ch) << '\'';
776 return true;
777 }
778}
779
780inline bool JSONReader::NextArrayItem() {
781 bool next = true;
782 if (scope_counter_.back() != 0) {
783 int ch = NextNonSpace();
784 if (ch == EOF) {
785 next = false;
786 } else if (ch == ']') {
787 next = false;
788 } else {
789 CHECK_EQ(ch, ',')
790 << "Error at" << line_info()
791 << ", JSON array expect \']\' or \',\'. Get \'" << static_cast<char>(ch) << "\' instead";
792 }
793 } else {
794 int ch = PeekNextNonSpace();
795 if (ch == ']') {
796 NextChar();
797 next = false;
798 }
799 }
800 if (!next) {
801 scope_counter_.pop_back();
802 return false;
803 } else {
804 scope_counter_.back() += 1;
805 return true;
806 }
807}
808
809template<typename ValueType>
810inline void JSONReader::Read(ValueType *out_value) {
811 json::Handler<ValueType>::Read(this, out_value);
812}
813
814inline void JSONWriter::WriteNoEscape(const std::string &s) {
815 Extend(os_, '\"');
816 Extend(os_, s);
817 Extend(os_, '\"');
818}
819
820inline void JSONWriter::WriteString(const std::string &s) {
821 Extend(os_, '\"');
822 for (size_t i = 0; i < s.length(); ++i) {
823 char ch = s[i];
824 switch (ch) {
825 case '\r': Extend(os_, "\\r"); break;
826 case '\n': Extend(os_, "\\n"); break;
827 case '\\': Extend(os_, "\\\\"); break;
828 case '\t': Extend(os_, "\\t"); break;
829 case '\"': Extend(os_, "\\\""); break;
830 default: Extend(os_, ch);
831 }
832 }
833 Extend(os_, '\"');
834}
835
836template<typename ValueType>
837inline void JSONWriter::WriteNumber(const ValueType &v) {
838#ifndef _LIBCPP_SGX_NO_IOSTREAMS
839 Extend(os_, v);
840#else
841 Extend(os_, std::to_string(v));
842#endif
843}
844
845inline void JSONWriter::BeginArray(bool multi_line) {
846 Extend(os_, '[');
847 scope_multi_line_.push_back(multi_line);
848 scope_counter_.push_back(0);
849}
850
851inline void JSONWriter::EndArray() {
852 CHECK_NE(scope_multi_line_.size(), 0U);
853 CHECK_NE(scope_counter_.size(), 0U);
854 bool newline = scope_multi_line_.back();
855 size_t nelem = scope_counter_.back();
856 scope_multi_line_.pop_back();
857 scope_counter_.pop_back();
858 if (newline && nelem != 0) WriteSeperator();
859 Extend(os_, ']');
860}
861
862inline void JSONWriter::BeginObject(bool multi_line) {
863 Extend(os_, '{');
864 scope_multi_line_.push_back(multi_line);
865 scope_counter_.push_back(0);
866}
867
868inline void JSONWriter::EndObject() {
869 CHECK_NE(scope_multi_line_.size(), 0U);
870 CHECK_NE(scope_counter_.size(), 0U);
871 bool newline = scope_multi_line_.back();
872 size_t nelem = scope_counter_.back();
873 scope_multi_line_.pop_back();
874 scope_counter_.pop_back();
875 if (newline && nelem != 0) WriteSeperator();
876 Extend(os_, '}');
877}
878
879template<typename ValueType>
880inline void JSONWriter::WriteObjectKeyValue(const std::string &key,
881 const ValueType &value) {
882 if (scope_counter_.back() > 0) {
883 Extend(os_, ", ");
884 }
885 WriteSeperator();
886 Extend(os_, '\"');
887 Extend(os_, key);
888 Extend(os_, "\": ");
889 scope_counter_.back() += 1;
890 json::Handler<ValueType>::Write(this, value);
891}
892
893inline void JSONWriter::WriteArraySeperator() {
894 if (scope_counter_.back() != 0) {
895 Extend(os_, ", ");
896 }
897 scope_counter_.back() += 1;
898 WriteSeperator();
899}
900
901template<typename ValueType>
902inline void JSONWriter::WriteArrayItem(const ValueType &value) {
903 this->WriteArraySeperator();
904 json::Handler<ValueType>::Write(this, value);
905}
906
907template<typename ValueType>
908inline void JSONWriter::Write(const ValueType &value) {
909 size_t nscope = scope_multi_line_.size();
910 json::Handler<ValueType>::Write(this, value);
911 CHECK_EQ(nscope, scope_multi_line_.size())
912 << "Uneven scope, did you call EndArray/EndObject after each BeginObject/Array?";
913}
914
915inline void JSONWriter::WriteSeperator() {
916 if (scope_multi_line_.size() == 0 || scope_multi_line_.back()) {
917 Extend(os_, '\n');
918 Extend(os_, std::string(scope_multi_line_.size() * 2, ' '));
919 }
920}
921
922inline void JSONObjectReadHelper::ReadAllFields(JSONReader *reader) {
923 reader->BeginObject();
924 std::map<std::string, int> visited;
925 std::string key;
926 while (reader->NextObjectItem(&key)) {
927 if (map_.count(key) != 0) {
928 Entry e = map_[key];
929 (*e.func)(reader, e.addr);
930 visited[key] = 0;
931 } else {
932#ifndef _LIBCPP_SGX_NO_IOSTREAMS
933 std::ostringstream err;
934#else
935 std::string err("");
936#endif
937 Extend(&err, "JSONReader: Unknown field ");
938 Extend(&err, key);
939 Extend(&err, ", candidates are: \n");
940 for (std::map<std::string, Entry>::iterator
941 it = map_.begin(); it != map_.end(); ++it) {
942 Extend(&err, '\"');
943 Extend(&err, it->first);
944 Extend(&err, "\"\n");
945 }
946#ifndef _LIBCPP_SGX_NO_IOSTREAMS
947 LOG(FATAL) << err.str();
948#else
949 LOG(FATAL) << err;
950#endif
951 }
952 }
953 if (visited.size() != map_.size()) {
954 for (std::map<std::string, Entry>::iterator
955 it = map_.begin(); it != map_.end(); ++it) {
956 if (it->second.optional) continue;
957 CHECK_NE(visited.count(it->first), 0U)
958 << "JSONReader: Missing field \"" << it->first << "\"\n At "
959 << reader->line_info();
960 }
961 }
962}
963
964template<typename T>
965inline void JSONObjectReadHelper::ReaderFunction(JSONReader *reader, void *addr) {
966 json::Handler<T>::Read(reader, static_cast<T*>(addr));
967}
968
969template<typename T>
970inline void JSONObjectReadHelper::
971DeclareFieldInternal(const std::string &key, T *addr, bool optional) {
972 CHECK_EQ(map_.count(key), 0U)
973 << "Adding duplicate field " << key;
974 Entry e;
975 e.func = ReaderFunction<T>;
976 e.addr = static_cast<void*>(addr);
977 e.optional = optional;
978 map_[key] = e;
979}
980
981//! \endcond
982} // namespace dmlc
983#endif // DMLC_JSON_H_
984