26 #include <initializer_list>
34 #include <type_traits>
37 #include "marley/marley_utils.hh"
38 #include "marley/Error.hh"
39 #include "marley/Logger.hh"
44 std::string json_escape(
const std::string& str ) {
46 for(
unsigned i = 0; i < str.length(); ++i )
48 case '\"': output +=
"\\\"";
break;
49 case '\\': output +=
"\\\\";
break;
50 case '\b': output +=
"\\b";
break;
51 case '\f': output +=
"\\f";
break;
52 case '\n': output +=
"\\n";
break;
53 case '\r': output +=
"\\r";
break;
54 case '\t': output +=
"\\t";
break;
55 default : output += str[i];
break;
64 Data(
double d) : float_(d) {}
65 Data(
long l) : integer_(l) {}
66 Data(
bool b) : boolean_(b) {}
67 Data(
const std::string& s) : string_(
new std::string(s)) {}
68 Data() : integer_(0) {}
70 std::deque<JSON>* list_;
71 std::map<std::string, JSON>* map_;
98 typename Container::iterator begin() {
99 return object ?
object->begin() :
typename Container::iterator();
101 typename Container::iterator end() {
102 return object ?
object->end() :
typename Container::iterator();
104 typename Container::const_iterator begin()
const {
105 return object ?
object->begin() :
typename Container::iterator();
107 typename Container::const_iterator end()
const {
108 return object ?
object->end() :
typename Container::iterator();
112 template <
typename Container>
116 const Container* object;
122 typename Container::const_iterator begin()
const
123 {
return object ?
object->begin()
124 :
typename Container::const_iterator(); }
125 typename Container::const_iterator end()
const
126 {
return object ?
object->end()
127 :
typename Container::const_iterator(); }
130 JSON() : data_(), type_(DataType::Null) {}
132 JSON(std::initializer_list<JSON> list) :
JSON()
134 set_type(DataType::Object);
135 for(
auto i = list.begin(), e = list.end(); i != e; ++i, ++i)
136 operator[](i->to_string()) = *std::next(i);
139 JSON(JSON&& other) : data_(other.data_), type_(other.type_)
140 { other.type_ = DataType::Null; other.data_.map_ =
nullptr; }
142 JSON& operator=(JSON&& other) {
145 other.data_.map_ =
nullptr;
146 other.type_ = DataType::Null;
150 JSON(
const JSON& other) {
151 switch(other.type_) {
152 case DataType::Object:
153 data_.map_ =
new std::map<std::string,JSON>(
154 other.data_.map_->begin(), other.data_.map_->end());
156 case DataType::Array:
157 data_.list_ =
new std::deque<JSON>(other.data_.list_->begin(),
158 other.data_.list_->end());
160 case DataType::String:
161 data_.string_ =
new std::string(*other.data_.string_);
169 JSON& operator=(
const JSON& other) {
170 switch(other.type_) {
171 case DataType::Object:
172 data_.map_ =
new std::map<std::string,JSON>(
173 other.data_.map_->begin(), other.data_.map_->end());
175 case DataType::Array:
176 data_.list_ =
new std::deque<JSON>( other.data_.list_->begin(),
177 other.data_.list_->end());
179 case DataType::String:
180 data_.string_ =
new std::string(*other.data_.string_);
191 case DataType::Array:
194 case DataType::Object:
197 case DataType::String:
198 delete data_.string_;
204 template <
typename T> JSON(T b,
205 typename std::enable_if<std::is_same<T,bool>::value>::type* = 0)
206 : data_(b), type_(DataType::Boolean) {}
208 template <
typename T> JSON(T i,
209 typename std::enable_if<std::is_integral<T>::value
210 && !std::is_same<T,bool>::value>::type* = 0)
211 : data_(static_cast<long>(i)), type_(DataType::Integral) {}
213 template <
typename T> JSON(T f,
214 typename std::enable_if<std::is_floating_point<T>::value>::type* = 0)
215 : data_(static_cast<double>(f)), type_(DataType::Floating) {}
217 explicit JSON(
const std::string& s)
218 : data_(s), type_(DataType::String) {}
225 JSON(std::nullptr_t) : data_(), type_(DataType::Null) {}
227 static inline JSON make(DataType type) {
233 static inline JSON array() {
234 return JSON::make(JSON::DataType::Array);
237 template <
typename... T>
238 static JSON array( T... args )
240 JSON arr = JSON::make(JSON::DataType::Array);
245 static inline JSON object() {
246 return JSON::make(JSON::DataType::Object);
249 static inline JSON load(
const std::string& s);
250 static inline JSON load(std::istream& is);
251 static inline JSON load_file(
const std::string& s);
253 template <
typename T>
void append(T arg) {
254 set_type(DataType::Array);
255 data_.list_->emplace_back(arg);
258 template <
typename T,
typename... U>
void append(T arg, U... args) {
259 append(arg); append(args...);
262 template <
typename T>
263 typename std::enable_if<std::is_same<T,bool>::value, JSON&>::type
266 set_type(DataType::Boolean);
271 template <
typename T>
typename std::enable_if<std::is_integral<T>::value
272 && !std::is_same<T,bool>::value, JSON&>::type operator=(T i)
274 set_type( DataType::Integral );
279 template <
typename T>
280 typename std::enable_if<std::is_floating_point<T>::value, JSON&>::type
283 set_type(DataType::Floating);
288 template <
typename T>
typename std::enable_if<std::is_convertible<T,
289 std::string>::value, JSON&>::type operator=(T s)
291 set_type(DataType::String);
292 *data_.string_ = std::string(s);
296 JSON& operator[](
const std::string& key) {
297 set_type(DataType::Object);
298 return data_.map_->operator[](key);
301 JSON& operator[](
unsigned index) {
302 set_type(DataType::Array);
303 if (index >= data_.list_->size()) data_.list_->resize(index + 1);
304 return data_.list_->operator[](index);
307 JSON& at(
const std::string& key) {
308 return operator[](key);
311 const JSON& at(
const std::string &key)
const {
312 return data_.map_->at(key);
315 JSON& at(
unsigned index) {
316 return operator[](index);
319 const JSON& at(
unsigned index)
const {
320 return data_.list_->at(index);
324 if (type_ == DataType::Array)
return data_.list_->size();
328 bool has_key(
const std::string& key)
const {
329 if (type_ == DataType::Object)
330 return data_.map_->find( key ) != data_.map_->end();
335 if (type_ == DataType::Object)
336 return data_.map_->size();
337 else if (type_ == DataType::Array)
338 return data_.list_->size();
343 inline DataType type()
const {
return type_; }
346 inline bool is_null()
const {
return type_ == DataType::Null; }
347 inline bool is_object()
const {
return type_ == DataType::Object; }
348 inline bool is_array()
const {
return type_ == DataType::Array; }
349 inline bool is_string()
const {
return type_ == DataType::String; }
350 inline bool is_float()
const {
return type_ == DataType::Floating; }
351 inline bool is_integer()
const {
return type_ == DataType::Integral; }
352 inline bool is_bool()
const {
return type_ == DataType::Boolean; }
354 std::string to_string()
const {
359 std::string to_string(
bool& ok)
const {
360 ok = (type_ == DataType::String);
361 return ok ? json_escape(*data_.string_) : std::string(
"");
364 std::string to_string_or_throw()
const {
366 std::string result = to_string(ok);
367 if (!ok)
throw marley::Error(
"Failed to convert JSON value to string");
371 double to_double()
const {
376 double to_double(
bool& ok)
const {
377 ok = (type_ == DataType::Floating);
378 if (ok)
return data_.float_;
379 ok = (type_ == DataType::Integral);
380 if (ok)
return data_.integer_;
384 double to_double_or_throw()
const {
386 double result = to_double(ok);
387 if (!ok)
throw marley::Error(
"Failed to convert JSON value '"
388 + to_string() +
"' to double");
392 long to_long()
const {
397 long to_long(
bool& ok)
const {
398 ok = (type_ == DataType::Integral);
399 return ok ? data_.integer_ : 0;
402 long to_long_or_throw()
const {
404 double result = to_long(ok);
405 if (!ok)
throw marley::Error(
"Failed to convert JSON value '"
406 + to_string() +
"' to long");
410 bool to_bool()
const {
415 bool to_bool(
bool& ok)
const {
416 ok = (type_ == DataType::Boolean);
417 return ok ? data_.boolean_ :
false;
420 bool to_bool_or_throw()
const {
422 double result = to_bool(ok);
423 if (!ok)
throw marley::Error(
"Failed to convert JSON value '"
424 + to_string() +
"' to bool");
428 JSONWrapper<std::map<std::string,JSON> > object_range() {
429 if (type_ == DataType::Object)
430 return JSONWrapper<std::map<std::string,JSON>>(data_.map_);
431 else return JSONWrapper<std::map<std::string,JSON>>(
nullptr);
434 JSONWrapper<std::deque<JSON> > array_range() {
435 if (type_ == DataType::Array)
436 return JSONWrapper<std::deque<JSON>>(data_.list_);
437 else return JSONWrapper<std::deque<JSON>>(
nullptr);
440 JSONConstWrapper<std::map<std::string,JSON> > object_range()
const {
441 if (type_ == DataType::Object)
442 return JSONConstWrapper<std::map<std::string,JSON>>(data_.map_);
443 else return JSONConstWrapper<std::map<std::string,JSON>>(
nullptr);
447 JSONConstWrapper<std::deque<JSON>> array_range()
const {
448 if ( type_ == DataType::Array )
449 return JSONConstWrapper<std::deque<JSON>>(data_.list_);
450 else return JSONConstWrapper<std::deque<JSON>>(
nullptr);
456 std::string dump_string(
const int indent_step = -1)
const {
457 std::stringstream out;
460 if (indent_step >= 0)
461 print(out,
static_cast<unsigned int>(indent_step),
true);
463 else print(out, 0,
false);
471 void print(std::ostream& out,
const unsigned int indent_step,
472 bool pretty,
const unsigned int current_indent = 0)
const
478 static std::ostringstream out_float;
479 static bool set_precision =
false;
480 if (!set_precision) {
481 out_float.precision(std::numeric_limits<double>::max_digits10);
482 set_precision =
true;
485 unsigned int indent = current_indent;
491 case DataType::Object: {
494 indent += indent_step;
498 for(
auto &p : *data_.map_ ) {
501 if (pretty) out <<
'\n';
504 out << std::string(indent,
' ') <<
'\"'
505 << json_escape( p.first ) <<
'\"';
507 if (pretty) out <<
" : ";
510 p.second.print( out, indent_step, pretty, indent );
514 indent -= indent_step;
517 out << std::string(indent,
' ') +
'}';
520 case DataType::Array: {
523 indent += indent_step;
527 for(
auto &p : *data_.list_ ) {
530 if (pretty) out <<
'\n';
532 out << std::string(indent,
' ');
533 p.print( out, indent_step, pretty, indent );
537 indent -= indent_step;
540 out << std::string(indent,
' ') <<
']';
543 case DataType::String:
544 out <<
'\"' + json_escape( *data_.string_ ) +
'\"';
546 case DataType::Floating:
551 out_float << data_.float_;
553 out << out_float.str();
555 case DataType::Integral:
556 out << data_.integer_;
558 case DataType::Boolean:
559 out << (data_.boolean_ ?
"true" :
"false");
570 void check_if_object(
const std::string& key) {
571 if (type_ != DataType::Object)
throw marley::Error(
"Attempted"
572 " to retrieve a value for the key '" + key +
" from a JSON primitive"
573 " that is not an object");
576 void set_type(DataType type) {
577 if (type == type_)
return;
580 case DataType::Object:
583 case DataType::Array:
586 case DataType::String:
587 delete data_.string_;
594 data_.map_ =
nullptr;
596 case DataType::Object:
597 data_.map_ =
new std::map<std::string, JSON>();
599 case DataType::Array:
600 data_.list_ =
new std::deque<JSON>();
602 case DataType::String:
603 data_.string_ =
new std::string();
605 case DataType::Floating:
608 case DataType::Integral:
611 case DataType::Boolean:
612 data_.boolean_ =
false;
623 double get_double(
const std::string& key) {
624 check_if_object(key);
625 if (has_key(key))
return this->at(key).to_double_or_throw();
633 double get_double(
const std::string& key,
double default_value) {
634 check_if_object(key);
635 if (!has_key(key))
return default_value;
636 else return this->at(key).to_double_or_throw();
641 long get_long(
const std::string& key) {
642 check_if_object(key);
643 if (has_key(key))
return this->at(key).to_long_or_throw();
651 long get_long(
const std::string& key,
long default_value) {
652 check_if_object(key);
653 if (!has_key(key))
return default_value;
654 else return this->at(key).to_long_or_throw();
659 bool get_bool(
const std::string& key) {
660 check_if_object(key);
661 if (has_key(key))
return this->at(key).to_bool_or_throw();
669 bool get_bool(
const std::string& key,
bool default_value) {
670 check_if_object(key);
671 if (!has_key(key))
return default_value;
672 else return this->at(key).to_bool_or_throw();
677 std::string get_string(
const std::string& key) {
678 check_if_object(key);
679 if (has_key(key))
return this->at(key).to_string_or_throw();
681 return std::string(
"");
687 std::string get_string(
const std::string& key,
688 const std::string& default_value)
690 check_if_object(key);
691 if (!has_key(key))
return default_value;
692 else return this->at(key).to_string_or_throw();
697 marley::JSON get_object(
const std::string& key,
bool throw_error =
true)
699 check_if_object(key);
700 if (has_key(key))
return this->at(key);
701 else if (throw_error)
marley::Error(
"Missing JSON key '" + key +
'\'');
702 return JSON::make(JSON::DataType::Object);
707 DataType type_ = DataType::Null;
712 JSON parse_next(std::istream&);
714 void issue_parse_error(
char found_char,
const std::string& message)
716 std::string msg(message);
717 if (found_char == std::ifstream::traits_type::eof())
718 msg +=
"end-of-file";
719 else msg += std::string(
"\'") + found_char +
'\'';
724 void issue_parse_error(
const std::string& found_str,
725 const std::string& message,
const std::istream& is)
727 std::string msg(message);
728 if (!is) msg +=
"end-of-file";
729 else msg +=
'\'' + found_str +
'\'';
737 void skip_comment(std::istream& in,
bool is_multiline =
false) {
741 if (c ==
'*' && in.peek() ==
'/') {
748 else in.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
753 void skip_ws(std::istream& in,
char& read_char) {
754 while (read_char = in.get(), std::isspace(read_char))
continue;
755 if (read_char ==
'/') {
757 if (c ==
'/' || c ==
'*') {
758 read_char = in.get();
759 skip_comment(in, c ==
'*');
760 return skip_ws(in, read_char);
767 void consume_ws(std::istream& in) {
775 char get_next_char(std::istream& in)
782 JSON parse_object(std::istream& in) {
784 JSON
object = JSON::make(JSON::DataType::Object);
791 if ( in.peek() ==
'}' ) {
795 else if ( in.peek() ==
'\"' ) {
796 key = parse_next(in);
805 if (c ==
':' || std::isspace(c)) {
814 char next = get_next_char(in);
816 issue_parse_error(next,
"JSON object: Expected colon, found ");
821 JSON value = parse_next(in);
822 object[key.to_string()] = value;
824 next = get_next_char(in);
825 if ( next ==
',' )
continue;
826 else if ( next ==
'}' )
break;
828 issue_parse_error(next,
"JSON object: Expected comma, found ");
836 JSON parse_array(std::istream& in) {
837 JSON array = JSON::make(JSON::DataType::Array);
843 if (in.peek() ==
']') {
848 array[index++] = parse_next(in);
851 char next = in.get();
852 if (next ==
',')
continue;
853 else if (next ==
']')
break;
855 issue_parse_error(next,
"JSON array: Expected ',' or ']'"
857 return JSON::make(JSON::DataType::Array);
864 JSON parse_string(std::istream& in) {
867 for(
char c = in.get(); c !=
'\"' && in; c = in.get()) {
870 case '\"': val +=
'\"';
break;
871 case '\\': val +=
'\\';
break;
872 case '/' : val +=
'/' ;
break;
873 case 'b' : val +=
'\b';
break;
874 case 'f' : val +=
'\f';
break;
875 case 'n' : val +=
'\n';
break;
876 case 'r' : val +=
'\r';
break;
877 case 't' : val +=
'\t';
break;
880 for(
unsigned i = 1; i <= 4; ++i) {
882 if ((c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f')
883 || (c >=
'A' && c <=
'F')) val += c;
885 issue_parse_error(c,
"JSON string: Expected hex character"
886 " in unicode escape, found ");
887 return JSON::make(JSON::DataType::String);
892 default: val +=
'\\';
break;
902 JSON parse_number(std::istream& in,
char old) {
904 std::string val, exp_str;
906 bool isDouble =
false;
909 if ( (c ==
'-') || (c >=
'0' && c <=
'9') )
911 else if ( c ==
'.' ) {
919 if ( c ==
'E' || c ==
'e' ) {
920 if ( in.peek() ==
'-' ){ in.ignore(); exp_str +=
'-';}
923 if ( c >=
'0' && c <=
'9' )
925 else if ( !std::isspace( c ) && c !=
',' && c !=
']' && c !=
'}' ) {
926 issue_parse_error(c,
"JSON number: Expected a number for"
927 " exponent, found ");
928 return JSON::make(JSON::DataType::Null);
933 exp = std::stol( exp_str );
935 else if ( !std::isspace( c ) && c !=
',' && c !=
']' && c !=
'}' ) {
936 issue_parse_error(c,
"JSON number: unexpected character ");
937 return JSON::make(JSON::DataType::Null);
942 Number = std::stod( val ) * std::pow( 10, exp );
944 if ( !exp_str.empty() )
945 Number = std::stol( val ) * std::pow( 10, exp );
947 Number = std::stol( val );
952 JSON parse_bool(std::istream& in,
char old) {
954 std::string s(1, old);
956 for (
size_t i = 0; i < 3; ++i) s += in.get();
957 if (s ==
"true") b =
true;
959 else if (old ==
'f') {
960 for (
size_t i = 0; i < 4; ++i) s += in.get();
961 if (s ==
"false") b =
false;
963 if (b.type() == JSON::DataType::Null) {
965 while (in.good() && !std::isspace(in.peek())) s += in.get();
966 marley_utils::trim_inplace(s);
968 issue_parse_error(s,
"JSON bool: Expected 'true' or 'false', found ",
970 return JSON::make(JSON::DataType::Null);
975 JSON parse_null(std::istream& in) {
977 std::string s(1,
'n');
978 for (
size_t i = 0; i < 3; ++i) s += in.get();
980 issue_parse_error(
"JSON null: Expected 'null', found ", s, in);
981 return JSON::make(JSON::DataType::Null);
986 JSON parse_next(std::istream& in) {
987 char value = get_next_char(in);
989 case '[' :
return parse_array(in);
990 case '{' :
return parse_object(in);
991 case '\"':
return parse_string(in);
993 case 'f' :
return parse_bool(in, value);
994 case 'n' :
return parse_null(in);
996 if ((value <= '9' && value >=
'0') || value ==
'-')
997 return parse_number(in, value);
1000 if (!in)
throw marley::Error(
"Unexpected end of JSON configuration"
1003 +
" Unknown starting character '" + value +
"'\n");
1008 inline JSON JSON::load_file(
const std::string& filename) {
1009 std::ifstream in(filename);
1010 if (in.good())
return load(in);
1012 throw marley::Error(
"Could not open the file \"" + filename +
"\"");
1013 return JSON::make(JSON::DataType::Null);
1017 inline JSON JSON::load(std::istream& in) {
1018 char first = get_next_char(in);
1020 throw marley::Error(
"Missing '{' at beginning of JSON object");
1022 return parse_object(in);
1026 return parse_next(in);
1030 inline JSON JSON::load(
const std::string& str) {
1031 std::stringstream iss(str);
1038 inline std::ostream& operator<<(std::ostream &os,
const marley::JSON& json) {
1039 os << json.dump_string();
1043 inline std::istream& operator>>(std::istream &is,
marley::JSON& json) {
1044 json = marley::JSON::load(is);
Base class for all exceptions thrown by MARLEY functions.
Definition: Error.hh:32
bool is_null() const
Functions for getting primitives from the JSON object.
Definition: JSON.hh:346