MARLEY (Model of Argon Reaction Low Energy Yields)  v1.1.0
A Monte Carlo event generator for tens-of-MeV neutrino-nucleus interactions in liquid argon
 All Classes Functions Variables Enumerations Pages
JSON.hh
1 // Based on https://github.com/lefticus/SimpleJSON
2 #pragma once
3 
4 // standard library includes
5 #include <cctype>
6 #include <cmath>
7 #include <cstdint>
8 #include <deque>
9 #include <initializer_list>
10 #include <iostream>
11 #include <istream>
12 #include <ostream>
13 #include <limits>
14 #include <sstream>
15 #include <map>
16 #include <string>
17 #include <type_traits>
18 
19 // MARLEY includes
20 #include "marley/marley_utils.hh"
21 #include "marley/Error.hh"
22 #include "marley/Logger.hh"
23 
24 namespace marley {
25 
26  namespace {
27  std::string json_escape( const std::string& str ) {
28  std::string output;
29  for( unsigned i = 0; i < str.length(); ++i )
30  switch( str[i] ) {
31  case '\"': output += "\\\""; break;
32  case '\\': output += "\\\\"; break;
33  case '\b': output += "\\b"; break;
34  case '\f': output += "\\f"; break;
35  case '\n': output += "\\n"; break;
36  case '\r': output += "\\r"; break;
37  case '\t': output += "\\t"; break;
38  default : output += str[i]; break;
39  }
40  return output;
41  }
42  }
43 
44  class JSON
45  {
46  union Data {
47  Data(double d) : float_(d) {}
48  Data(long l) : integer_(l) {}
49  Data(bool b) : boolean_(b) {}
50  Data(const std::string& s) : string_(new std::string(s)) {}
51  Data() : integer_(0) {}
52 
53  std::deque<JSON>* list_;
54  std::map<std::string, JSON>* map_;
55  std::string* string_;
56  double float_;
57  long integer_;
58  bool boolean_;
59  } data_;
60 
61  public:
62  enum class DataType {
63  Null,
64  Object,
65  Array,
66  String,
67  Floating,
68  Integral,
69  Boolean
70  };
71 
72  template <typename Container> class JSONWrapper {
73 
74  private:
75  Container* object;
76 
77  public:
78  JSONWrapper(Container* val) : object(val) {}
79  JSONWrapper(std::nullptr_t) : object(nullptr) {}
80 
81  typename Container::iterator begin() {
82  return object ? object->begin() : typename Container::iterator();
83  }
84  typename Container::iterator end() {
85  return object ? object->end() : typename Container::iterator();
86  }
87  typename Container::const_iterator begin() const {
88  return object ? object->begin() : typename Container::iterator();
89  }
90  typename Container::const_iterator end() const {
91  return object ? object->end() : typename Container::iterator();
92  }
93  };
94 
95  template <typename Container>
97 
98  private:
99  const Container* object;
100 
101  public:
102  JSONConstWrapper(const Container* val) : object(val) {}
103  JSONConstWrapper(std::nullptr_t) : object(nullptr) {}
104 
105  typename Container::const_iterator begin() const
106  { return object ? object->begin()
107  : typename Container::const_iterator(); }
108  typename Container::const_iterator end() const
109  { return object ? object->end()
110  : typename Container::const_iterator(); }
111  };
112 
113  JSON() : data_(), type_(DataType::Null) {}
114 
115  JSON(std::initializer_list<JSON> list) : JSON()
116  {
117  set_type(DataType::Object);
118  for(auto i = list.begin(), e = list.end(); i != e; ++i, ++i)
119  operator[](i->to_string()) = *std::next(i);
120  }
121 
122  JSON(JSON&& other) : data_(other.data_), type_(other.type_)
123  { other.type_ = DataType::Null; other.data_.map_ = nullptr; }
124 
125  JSON& operator=(JSON&& other) {
126  data_ = other.data_;
127  type_ = other.type_;
128  other.data_.map_ = nullptr;
129  other.type_ = DataType::Null;
130  return *this;
131  }
132 
133  JSON(const JSON& other) {
134  switch(other.type_) {
135  case DataType::Object:
136  data_.map_ = new std::map<std::string,JSON>(
137  other.data_.map_->begin(), other.data_.map_->end());
138  break;
139  case DataType::Array:
140  data_.list_ = new std::deque<JSON>(other.data_.list_->begin(),
141  other.data_.list_->end());
142  break;
143  case DataType::String:
144  data_.string_ = new std::string(*other.data_.string_);
145  break;
146  default:
147  data_ = other.data_;
148  }
149  type_ = other.type_;
150  }
151 
152  JSON& operator=(const JSON& other) {
153  switch(other.type_) {
154  case DataType::Object:
155  data_.map_ = new std::map<std::string,JSON>(
156  other.data_.map_->begin(), other.data_.map_->end());
157  break;
158  case DataType::Array:
159  data_.list_ = new std::deque<JSON>( other.data_.list_->begin(),
160  other.data_.list_->end());
161  break;
162  case DataType::String:
163  data_.string_ = new std::string(*other.data_.string_);
164  break;
165  default:
166  data_ = other.data_;
167  }
168  type_ = other.type_;
169  return *this;
170  }
171 
172  ~JSON() {
173  switch(type_) {
174  case DataType::Array:
175  delete data_.list_;
176  break;
177  case DataType::Object:
178  delete data_.map_;
179  break;
180  case DataType::String:
181  delete data_.string_;
182  break;
183  default:;
184  }
185  }
186 
187  template <typename T> JSON(T b,
188  typename std::enable_if<std::is_same<T,bool>::value>::type* = 0)
189  : data_(b), type_(DataType::Boolean) {}
190 
191  template <typename T> JSON(T i,
192  typename std::enable_if<std::is_integral<T>::value
193  && !std::is_same<T,bool>::value>::type* = 0)
194  : data_(static_cast<long>(i)), type_(DataType::Integral) {}
195 
196  template <typename T> JSON(T f,
197  typename std::enable_if<std::is_floating_point<T>::value>::type* = 0)
198  : data_(static_cast<double>(f)), type_(DataType::Floating) {}
199 
200  template <typename T> JSON(T s,
201  typename std::enable_if<std::is_convertible<T,
202  std::string>::value>::type* = 0) : data_(std::string(s)),
203  type_(DataType::String) {}
204 
205  JSON(std::nullptr_t) : data_(), type_(DataType::Null) {}
206 
207  static inline JSON make(DataType type) {
208  JSON ret;
209  ret.set_type(type);
210  return ret;
211  }
212 
213  static inline JSON array() {
214  return JSON::make(JSON::DataType::Array);
215  }
216 
217  template <typename... T>
218  static JSON array( T... args )
219  {
220  JSON arr = JSON::make(JSON::DataType::Array);
221  arr.append(args...);
222  return arr;
223  }
224 
225  static inline JSON object() {
226  return JSON::make(JSON::DataType::Object);
227  }
228 
229  static inline JSON load(const std::string& s);
230  static inline JSON load(std::istream& is);
231  static inline JSON load_file(const std::string& s);
232 
233  template <typename T> void append(T arg) {
234  set_type(DataType::Array);
235  data_.list_->emplace_back(arg);
236  }
237 
238  template <typename T, typename... U> void append(T arg, U... args) {
239  append(arg); append(args...);
240  }
241 
242  template <typename T>
243  typename std::enable_if<std::is_same<T,bool>::value, JSON&>::type
244  operator=(T b)
245  {
246  set_type(DataType::Boolean);
247  data_.boolean_ = b;
248  return *this;
249  }
250 
251  template <typename T> typename std::enable_if<std::is_integral<T>::value
252  && !std::is_same<T,bool>::value, JSON&>::type operator=(T i)
253  {
254  set_type( DataType::Integral );
255  data_.integer_ = i;
256  return *this;
257  }
258 
259  template <typename T>
260  typename std::enable_if<std::is_floating_point<T>::value, JSON&>::type
261  operator=(T f)
262  {
263  set_type(DataType::Floating);
264  data_.float_ = f;
265  return *this;
266  }
267 
268  template <typename T> typename std::enable_if<std::is_convertible<T,
269  std::string>::value, JSON&>::type operator=(T s)
270  {
271  set_type(DataType::String);
272  *data_.string_ = std::string(s);
273  return *this;
274  }
275 
276  JSON& operator[](const std::string& key) {
277  set_type(DataType::Object);
278  return data_.map_->operator[](key);
279  }
280 
281  JSON& operator[](unsigned index) {
282  set_type(DataType::Array);
283  if (index >= data_.list_->size()) data_.list_->resize(index + 1);
284  return data_.list_->operator[](index);
285  }
286 
287  JSON& at(const std::string& key) {
288  return operator[](key);
289  }
290 
291  const JSON& at(const std::string &key) const {
292  return data_.map_->at(key);
293  }
294 
295  JSON& at(unsigned index) {
296  return operator[](index);
297  }
298 
299  const JSON& at(unsigned index) const {
300  return data_.list_->at(index);
301  }
302 
303  int length() const {
304  if (type_ == DataType::Array) return data_.list_->size();
305  else return -1;
306  }
307 
308  bool has_key(const std::string& key) const {
309  if (type_ == DataType::Object)
310  return data_.map_->find( key ) != data_.map_->end();
311  else return false;
312  }
313 
314  int size() const {
315  if (type_ == DataType::Object)
316  return data_.map_->size();
317  else if (type_ == DataType::Array)
318  return data_.list_->size();
319  else
320  return -1;
321  }
322 
323  inline DataType type() const { return type_; }
324 
326  inline bool is_null() const { return type_ == DataType::Null; }
327  inline bool is_object() const { return type_ == DataType::Object; }
328  inline bool is_array() const { return type_ == DataType::Array; }
329  inline bool is_string() const { return type_ == DataType::String; }
330  inline bool is_float() const { return type_ == DataType::Floating; }
331  inline bool is_integer() const { return type_ == DataType::Integral; }
332  inline bool is_bool() const { return type_ == DataType::Boolean; }
333 
334  std::string to_string() const {
335  bool b;
336  return to_string(b);
337  }
338 
339  std::string to_string(bool& ok) const {
340  ok = (type_ == DataType::String);
341  return ok ? json_escape(*data_.string_) : std::string("");
342  }
343 
344  std::string to_string_or_throw() const {
345  bool ok;
346  std::string result = to_string(ok);
347  if (!ok) throw marley::Error("Failed to convert JSON value to string");
348  return result;
349  }
350 
351  double to_double() const {
352  bool b;
353  return to_double(b);
354  }
355 
356  double to_double(bool& ok) const {
357  ok = (type_ == DataType::Floating);
358  if (ok) return data_.float_;
359  ok = (type_ == DataType::Integral);
360  if (ok) return data_.integer_;
361  return 0.;
362  }
363 
364  double to_double_or_throw() const {
365  bool ok;
366  double result = to_double(ok);
367  if (!ok) throw marley::Error("Failed to convert JSON value '"
368  + to_string() + "' to double");
369  return result;
370  }
371 
372  long to_long() const {
373  bool b;
374  return to_long( b );
375  }
376 
377  long to_long(bool& ok) const {
378  ok = (type_ == DataType::Integral);
379  return ok ? data_.integer_ : 0;
380  }
381 
382  long to_long_or_throw() const {
383  bool ok;
384  double result = to_long(ok);
385  if (!ok) throw marley::Error("Failed to convert JSON value '"
386  + to_string() + "' to long");
387  return result;
388  }
389 
390  bool to_bool() const {
391  bool b;
392  return to_bool( b );
393  }
394 
395  bool to_bool(bool& ok) const {
396  ok = (type_ == DataType::Boolean);
397  return ok ? data_.boolean_ : false;
398  }
399 
400  bool to_bool_or_throw() const {
401  bool ok;
402  double result = to_bool(ok);
403  if (!ok) throw marley::Error("Failed to convert JSON value '"
404  + to_string() + "' to bool");
405  return result;
406  }
407 
408  JSONWrapper<std::map<std::string,JSON> > object_range() {
409  if (type_ == DataType::Object)
410  return JSONWrapper<std::map<std::string,JSON>>(data_.map_);
411  else return JSONWrapper<std::map<std::string,JSON>>(nullptr);
412  }
413 
414  JSONWrapper<std::deque<JSON> > array_range() {
415  if (type_ == DataType::Array)
416  return JSONWrapper<std::deque<JSON>>(data_.list_);
417  else return JSONWrapper<std::deque<JSON>>(nullptr);
418  }
419 
420  JSONConstWrapper<std::map<std::string,JSON> > object_range() const {
421  if (type_ == DataType::Object)
422  return JSONConstWrapper<std::map<std::string,JSON>>(data_.map_);
423  else return JSONConstWrapper<std::map<std::string,JSON>>(nullptr);
424  }
425 
426 
427  JSONConstWrapper<std::deque<JSON>> array_range() const {
428  if ( type_ == DataType::Array )
429  return JSONConstWrapper<std::deque<JSON>>(data_.list_);
430  else return JSONConstWrapper<std::deque<JSON>>(nullptr);
431  }
432 
433  // Portions of the serialization functions (dump_string, print)
434  // are based on techniques used in the JSON for Modern C++
435  // library by Niels Lohmann (https://github.com/nlohmann/json).
436  std::string dump_string(const int indent_step = -1) const {
437  std::stringstream out;
438  // Enable pretty-printing if the user specified a nonnegative
439  // indent_step value
440  if (indent_step >= 0)
441  print(out, static_cast<unsigned int>(indent_step), true);
442  // Otherwise, print the JSON object in the most compact form possible
443  else print(out, 0, false);
444 
445  // Return the completed JSON string
446  return out.str();
447  }
448 
449  // Implementation of serialization to text. Used by the public
450  // dump_string() method.
451  void print(std::ostream& out, const unsigned int indent_step,
452  bool pretty, const unsigned int current_indent = 0) const
453  {
454  // Use max_digits10 for outputting double-precision floating-point
455  // numbers. This ensures that repeated input/output via JSON will
456  // not result in any loss of precision. For more information, please
457  // see http://tinyurl.com/p8wyhnn
458  static std::ostringstream out_float;
459  static bool set_precision = false;
460  if (!set_precision) {
461  out_float.precision(std::numeric_limits<double>::max_digits10);
462  set_precision = true;
463  }
464 
465  unsigned int indent = current_indent;
466 
467  switch( type_ ) {
468  case DataType::Null:
469  out << "null";
470  return;
471  case DataType::Object: {
472  out << '{';
473  if (pretty) {
474  indent += indent_step;
475  out << '\n';
476  }
477  bool skip = true;
478  for( auto &p : *data_.map_ ) {
479  if ( !skip ) {
480  out << ',';
481  if (pretty) out << '\n';
482  }
483 
484  out << std::string(indent, ' ') << '\"'
485  << json_escape( p.first ) << '\"';
486 
487  if (pretty) out << " : ";
488  else out << ':';
489 
490  p.second.print( out, indent_step, pretty, indent );
491  skip = false;
492  }
493  if (pretty) {
494  indent -= indent_step;
495  out << '\n';
496  }
497  out << std::string(indent, ' ') + '}';
498  return;
499  }
500  case DataType::Array: {
501  out << '[';
502  if (pretty) {
503  indent += indent_step;
504  out << '\n';
505  }
506  bool skip = true;
507  for( auto &p : *data_.list_ ) {
508  if ( !skip ) {
509  out << ',';
510  if (pretty) out << '\n';
511  }
512  out << std::string(indent, ' ');
513  p.print( out, indent_step, pretty, indent );
514  skip = false;
515  }
516  if (pretty) {
517  indent -= indent_step;
518  out << '\n';
519  }
520  out << std::string(indent, ' ') << ']';
521  return;
522  }
523  case DataType::String:
524  out << '\"' + json_escape( *data_.string_ ) + '\"';
525  return;
526  case DataType::Floating:
527  // Clear any previous contents of the stringstream
528  out_float.str("");
529  out_float.clear();
530  // Fill it with the new floating-point number
531  out_float << data_.float_;
532  // Output the resulting string to the stream
533  out << out_float.str();
534  return;
535  case DataType::Integral:
536  out << data_.integer_;
537  return;
538  case DataType::Boolean:
539  out << (data_.boolean_ ? "true" : "false");
540  return;
541  default:
542  break;
543  }
544 
545  return;
546  }
547 
548  private:
549 
550  void check_if_object(const std::string& key) {
551  if (type_ != DataType::Object) throw marley::Error("Attempted"
552  " to retrieve a value for the key '" + key + " from a JSON primitive"
553  " that is not an object");
554  }
555 
556  void set_type(DataType type) {
557  if (type == type_) return;
558 
559  switch(type_) {
560  case DataType::Object:
561  delete data_.map_;
562  break;
563  case DataType::Array:
564  delete data_.list_;
565  break;
566  case DataType::String:
567  delete data_.string_;
568  break;
569  default:;
570  }
571 
572  switch(type) {
573  case DataType::Null:
574  data_.map_ = nullptr;
575  break;
576  case DataType::Object:
577  data_.map_ = new std::map<std::string, JSON>();
578  break;
579  case DataType::Array:
580  data_.list_ = new std::deque<JSON>();
581  break;
582  case DataType::String:
583  data_.string_ = new std::string();
584  break;
585  case DataType::Floating:
586  data_.float_ = 0.;
587  break;
588  case DataType::Integral:
589  data_.integer_ = 0;
590  break;
591  case DataType::Boolean:
592  data_.boolean_ = false;
593  break;
594  }
595 
596  type_ = type;
597  }
598 
599  public:
600 
601  // Attempts to get a floating point number from a JSON object with
602  // a given key. If the attempt fails, throw a marley::Error.
603  double get_double(const std::string& key) {
604  check_if_object(key);
605  if (has_key(key)) return this->at(key).to_double_or_throw();
606  else throw marley::Error("Missing JSON key '" + key + '\'');
607  return 0.;
608  }
609 
610  // Attempts to get a floating point number from a JSON object with
611  // a given key. If the key doesn't exist, use a default value. If a
612  // conversion attempt fails, throw a marley::Error.
613  double get_double(const std::string& key, double default_value) {
614  check_if_object(key);
615  if (!has_key(key)) return default_value;
616  else return this->at(key).to_double_or_throw();
617  }
618 
619  // Attempts to get an integer from a JSON object with
620  // a given key. If the attempt fails, throw a marley::Error.
621  long get_long(const std::string& key) {
622  check_if_object(key);
623  if (has_key(key)) return this->at(key).to_long_or_throw();
624  else throw marley::Error("Missing JSON key '" + key + '\'');
625  return 0.;
626  }
627 
628  // Attempts to get an integer from a JSON object with
629  // a given key. If the key doesn't exist, use a default value. If a
630  // conversion attempt fails, throw a marley::Error.
631  long get_long(const std::string& key, long default_value) {
632  check_if_object(key);
633  if (!has_key(key)) return default_value;
634  else return this->at(key).to_long_or_throw();
635  }
636 
637  // Attempts to get a bool from a JSON object with
638  // a given key. If the attempt fails, throw a marley::Error.
639  bool get_bool(const std::string& key) {
640  check_if_object(key);
641  if (has_key(key)) return this->at(key).to_bool_or_throw();
642  else throw marley::Error("Missing JSON key '" + key + '\'');
643  return 0.;
644  }
645 
646  // Attempts to get a bool from a JSON object with
647  // a given key. If the key doesn't exist, use a default value. If a
648  // conversion attempt fails, throw a marley::Error.
649  bool get_bool(const std::string& key, bool default_value) {
650  check_if_object(key);
651  if (!has_key(key)) return default_value;
652  else return this->at(key).to_bool_or_throw();
653  }
654 
655  // Attempts to get a string from a JSON object with
656  // a given key. If the attempt fails, throw a marley::Error.
657  std::string get_string(const std::string& key) {
658  check_if_object(key);
659  if (has_key(key)) return this->at(key).to_string_or_throw();
660  else throw marley::Error("Missing JSON key '" + key + '\'');
661  return std::string("");
662  }
663 
664  // Attempts to get a string from a JSON object with
665  // a given key. If the key doesn't exist, use a default value. If a
666  // conversion attempt fails, throw a marley::Error.
667  std::string get_string(const std::string& key,
668  const std::string& default_value)
669  {
670  check_if_object(key);
671  if (!has_key(key)) return default_value;
672  else return this->at(key).to_string_or_throw();
673  }
674 
675  // Copies a subobject from a JSON object with a given key. If the attempt
676  // fails, throw a marley::Error, unless the user asks us not to do so.
677  marley::JSON get_object(const std::string& key, bool throw_error = true)
678  {
679  check_if_object(key);
680  if (has_key(key)) return this->at(key);
681  else if (throw_error) marley::Error("Missing JSON key '" + key + '\'');
682  return JSON::make(JSON::DataType::Object);
683  }
684 
685  private:
686 
687  DataType type_ = DataType::Null;
688  };
689 
690  namespace {
691 
692  JSON parse_next(std::istream&);
693 
694  void issue_parse_warning(char found_char, const std::string& message)
695  {
696  std::string warning(message);
697  if (found_char == std::ifstream::traits_type::eof())
698  warning += "end-of-file";
699  else warning += std::string("\'") + found_char + '\'';
700  MARLEY_LOG_WARNING() << warning;
701  }
702 
703  void issue_parse_warning(const std::string& found_str,
704  const std::string& message, const std::istream& is)
705  {
706  std::string warning(message);
707  if (!is) warning += "end-of-file";
708  else warning += '\'' + found_str + '\'';
709  MARLEY_LOG_WARNING() << warning;
710  }
711 
712  // Skips single-line comments // and multi-line comments /* */
713  // These are technically not valid in JSON (the standard doesn't allow
714  // comments), but they are valid in Javascript object literals.
715  void skip_comment(std::istream& in, bool is_multiline = false) {
716  if (is_multiline) {
717  char c;
718  while (in.get(c)) {
719  if (c == '*' && in.peek() == '/') {
720  in.ignore();
721  break;
722  }
723  }
724  }
725  // Ignore all further characters until either a newline or end-of-file
726  else in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
727  }
728 
729  // Skips whitespace and comments, saving the last character read to
730  // read_char.
731  void skip_ws(std::istream& in, char& read_char) {
732  while (read_char = in.get(), std::isspace(read_char)) continue;
733  if (read_char == '/') {
734  char c = in.peek();
735  if (c == '/' || c == '*') {
736  read_char = in.get();
737  skip_comment(in, c == '*');
738  return skip_ws(in, read_char);
739  }
740  }
741  }
742 
743  // Removes whitespace and comments from the input stream, putting back
744  // the first non-whitespace and non-comment character it finds.
745  void consume_ws(std::istream& in) {
746  static char next;
747  skip_ws(in, next);
748  in.putback(next);
749  }
750 
751  // Removes whitespace and comments from the input stream, returning the
752  // first non-whitespace and non-comment character it finds.
753  char get_next_char(std::istream& in)
754  {
755  static char next;
756  skip_ws(in, next);
757  return next;
758  }
759 
760  JSON parse_object(std::istream& in) {
761 
762  JSON object = JSON::make(JSON::DataType::Object);
763 
764  for (;;) {
765 
766  consume_ws(in);
767  JSON key;
768 
769  if ( in.peek() == '}' ) {
770  in.ignore();
771  return object;
772  }
773  else if ( in.peek() == '\"' ) {
774  key = parse_next(in);
775  }
776  // The key isn't quoted, so assume it's a single word followed
777  // by a colon. Note that vanilla JSON requires all keys to be quoted,
778  // but Javascript object literals allow unquoted keys.
779  else {
780  std::string key_str;
781  char c;
782  while (in.get(c)) {
783  if (c == ':' || std::isspace(c)) {
784  in.putback(c);
785  break;
786  }
787  key_str += c;
788  }
789  key = key_str;
790  }
791 
792  char next = get_next_char(in);
793  if ( next != ':' ) {
794  issue_parse_warning(next, "JSON object: Expected colon, found ");
795  break;
796  }
797 
798  consume_ws(in);
799  JSON value = parse_next(in);
800  object[key.to_string()] = value;
801 
802  next = get_next_char(in);
803  if ( next == ',' ) continue;
804  else if ( next == '}' ) break;
805  else {
806  issue_parse_warning(next, "JSON object: Expected comma, found ");
807  break;
808  }
809  }
810 
811  return object;
812  }
813 
814  JSON parse_array(std::istream& in) {
815  JSON array = JSON::make(JSON::DataType::Array);
816  unsigned index = 0;
817 
818  for (;;) {
819 
820  consume_ws(in);
821  if (in.peek() == ']') {
822  in.ignore();
823  return array;
824  }
825 
826  array[index++] = parse_next(in);
827  consume_ws(in);
828 
829  char next = in.get();
830  if (next == ',') continue;
831  else if (next == ']') break;
832  else {
833  issue_parse_warning(next, "JSON array: Expected ',' or ']'"
834  ", found ");
835  return JSON::make(JSON::DataType::Array);
836  }
837  }
838 
839  return array;
840  }
841 
842  JSON parse_string(std::istream& in) {
843  JSON str;
844  std::string val;
845  for(char c = in.get(); c != '\"' && in; c = in.get()) {
846  if (c == '\\') {
847  switch( in.get() ) {
848  case '\"': val += '\"'; break;
849  case '\\': val += '\\'; break;
850  case '/' : val += '/' ; break;
851  case 'b' : val += '\b'; break;
852  case 'f' : val += '\f'; break;
853  case 'n' : val += '\n'; break;
854  case 'r' : val += '\r'; break;
855  case 't' : val += '\t'; break;
856  case 'u' : {
857  val += "\\u" ;
858  for(unsigned i = 1; i <= 4; ++i) {
859  c = in.get();
860  if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
861  || (c >= 'A' && c <= 'F')) val += c;
862  else {
863  issue_parse_warning(c, "JSON string: Expected hex character"
864  " in unicode escape, found ");
865  return JSON::make(JSON::DataType::String);
866  }
867  }
868  break;
869  }
870  default: val += '\\'; break;
871  }
872  }
873  else val += c;
874  }
875  str = val;
876  return str;
877  }
878 
879  //FIXME
880  JSON parse_number(std::istream& in, char old) {
881  JSON Number;
882  std::string val, exp_str;
883  char c = old;
884  bool isDouble = false;
885  long exp = 0;
886  for (;;) {
887  if ( (c == '-') || (c >= '0' && c <= '9') )
888  val += c;
889  else if ( c == '.' ) {
890  val += c;
891  isDouble = true;
892  }
893  else
894  break;
895  c = in.get();
896  }
897  if ( c == 'E' || c == 'e' ) {
898  if ( in.peek() == '-' ){ in.ignore(); exp_str += '-';}
899  for (;;) {
900  c = in.get();
901  if ( c >= '0' && c <= '9' )
902  exp_str += c;
903  else if ( !std::isspace( c ) && c != ',' && c != ']' && c != '}' ) {
904  issue_parse_warning(c, "JSON number: Expected a number for"
905  " exponent, found ");
906  return JSON::make(JSON::DataType::Null);
907  }
908  else
909  break;
910  }
911  exp = std::stol( exp_str );
912  }
913  else if ( !std::isspace( c ) && c != ',' && c != ']' && c != '}' ) {
914  issue_parse_warning(c, "JSON number: unexpected character ");
915  return JSON::make(JSON::DataType::Null);
916  }
917  in.putback(c);
918 
919  if ( isDouble )
920  Number = std::stod( val ) * std::pow( 10, exp );
921  else {
922  if ( !exp_str.empty() )
923  Number = std::stol( val ) * std::pow( 10, exp );
924  else
925  Number = std::stol( val );
926  }
927  return Number ;
928  }
929 
930  JSON parse_bool(std::istream& in, char old) {
931  JSON b;
932  std::string s(1, old);
933  if (old == 't') {
934  for (size_t i = 0; i < 3; ++i) s += in.get();
935  if (s == "true") b = true;
936  }
937  else if (old == 'f') {
938  for (size_t i = 0; i < 4; ++i) s += in.get();
939  if (s == "false") b = false;
940  }
941  if (b.type() == JSON::DataType::Null) {
942  // Get the entire string if the user supplied an invalid value
943  while (in.good() && !std::isspace(in.peek())) s += in.get();
944  marley_utils::trim_inplace(s);
945 
946  issue_parse_warning(s, "JSON bool: Expected 'true' or 'false', found ",
947  in);
948  return JSON::make(JSON::DataType::Null);
949  }
950  return b;
951  }
952 
953  JSON parse_null(std::istream& in) {
954  JSON null;
955  std::string s(1, 'n');
956  for (size_t i = 0; i < 3; ++i) s += in.get();
957  if ( s != "null") {
958  issue_parse_warning("JSON null: Expected 'null', found ", s, in);
959  return JSON::make(JSON::DataType::Null);
960  }
961  return null;
962  }
963 
964  JSON parse_next(std::istream& in) {
965  char value = get_next_char(in);
966  switch(value) {
967  case '[' : return parse_array(in);
968  case '{' : return parse_object(in);
969  case '\"': return parse_string(in);
970  case 't' :
971  case 'f' : return parse_bool(in, value);
972  case 'n' : return parse_null(in);
973  default :
974  if ((value <= '9' && value >= '0') || value == '-')
975  return parse_number(in, value);
976  }
977  // Exit gracefully if there was a problem, but complain a bit.
978  if (!in) MARLEY_LOG_WARNING() << "Unexpected end of JSON configuration"
979  << " file found\n";
980  else MARLEY_LOG_WARNING() << "JSON parse: Unknown starting character '"
981  << value << "'\n";
982  return JSON();
983  }
984  }
985 
986  inline JSON JSON::load_file(const std::string& filename) {
987  std::ifstream in(filename);
988  if (in.good()) return load(in);
989  else {
990  throw marley::Error("Could not open the file \"" + filename + "\"");
991  return JSON::make(JSON::DataType::Null);
992  }
993  }
994 
995  inline JSON JSON::load(std::istream& in) {
996  char first = get_next_char(in);
997  if (first != '{') {
998  MARLEY_LOG_WARNING() << "Missing '{' at beginning of JSON object";
999  in.putback(first);
1000  return parse_object(in);
1001  }
1002  else {
1003  in.putback(first);
1004  return parse_next(in);
1005  }
1006  }
1007 
1008  inline JSON JSON::load(const std::string& str) {
1009  std::stringstream iss(str);
1010  return load(iss);
1011  }
1012 
1013 }
1014 
1015 // Stream operators for JSON input and output using C++ streams
1016 inline std::ostream& operator<<(std::ostream &os, const marley::JSON& json) {
1017  os << json.dump_string();
1018  return os;
1019 }
1020 
1021 inline std::istream& operator>>(std::istream &is, marley::JSON& json) {
1022  json = marley::JSON::load(is);
1023  return is;
1024 }
Definition: JSON.hh:72
Definition: JSON.hh:44
Base class for all exceptions thrown by MARLEY functions.
Definition: Error.hh:16
bool is_null() const
Functions for getting primitives from the JSON object.
Definition: JSON.hh:326
Definition: JSON.hh:96