Apache Mesos
json.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_JSON__
14 #define __STOUT_JSON__
15 
16 // NOTE: This undef is necessary because we cannot reliably re-order the
17 // include statements in all cases. We define this flag globally since
18 // PicoJson requires it before importing <inttypes.h>. However, other
19 // libraries may import <inttypes.h> before we import <picojson.h>.
20 // Hence, we undefine the flag here to prevent the redefinition error.
21 #undef __STDC_FORMAT_MACROS
22 // We also need to define `PICOJSON_USE_INT64`, since we're
23 // unconditionally using the `picojson::value::get<uint64_t>()` and
24 // `picojson::value::is<uint64_t>()` functions below.
25 #define PICOJSON_USE_INT64
26 #include <picojson.h>
27 #define __STDC_FORMAT_MACROS
28 
29 #include <iomanip>
30 #include <iostream>
31 #include <limits>
32 #include <map>
33 #include <string>
34 #include <type_traits>
35 #include <vector>
36 
37 #include <boost/variant.hpp>
38 
39 #include <stout/check.hpp>
40 #include <stout/foreach.hpp>
41 #include <stout/jsonify.hpp>
42 #include <stout/numify.hpp>
43 #include <stout/result.hpp>
44 #include <stout/strings.hpp>
45 #include <stout/try.hpp>
46 #include <stout/unreachable.hpp>
47 
48 // TODO(benh): Replace the use of boost::variant here with our wrapper
49 // `Variant`.
50 
51 namespace JSON {
52 
53 // Implementation of the JavaScript Object Notation (JSON) grammar
54 // using boost::variant. We explicitly define each "type" of the
55 // grammar, including 'true' (json::True), 'false' (json::False), and
56 // 'null' (json::Null), for clarity and also because boost::variant
57 // "picks" the wrong type when we try and use std::string, long (or
58 // int), double (or float), and bool all in the same variant (while it
59 // does work with explicit casts, it seemed bad style to force people
60 // to put those casts in place). We could have avoided using
61 // json::String or json::Number and just used std::string and double
62 // respectively, but we choose to include them for completeness
63 // (although, this does pay a 2x cost when compiling thanks to all the
64 // extra template instantiations).
65 
66 // Note that all of these forward declarations are not necessary
67 // but it serves to document the set of types which are available.
68 struct String;
69 struct Number;
70 struct Object;
71 struct Array;
72 struct True;
73 struct False;
74 struct Boolean;
75 struct Null;
76 struct Value;
77 
78 
79 struct String
80 {
81  String() {}
82  String(const char* _value) : value(_value) {}
83  String(const std::string& _value) : value(_value) {}
84  std::string value;
85 };
86 
87 
88 // NOTE: Due to how PicoJson parses unsigned integers, a roundtrip from Number
89 // to JSON and back to Number will result in:
90 // - a signed integer, if the value is less than or equal to INT64_MAX;
91 // - or a double, if the value is greater than INT64_MAX.
92 // See: https://github.com/kazuho/picojson/blob/rel/v1.3.0/picojson.h#L777-L781
93 struct Number
94 {
95  Number() : value(0) {}
96 
97  template <typename T>
99  T _value,
100  typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0)
101  : type(FLOATING), value(_value) {}
102 
103  template <typename T>
105  T _value,
106  typename std::enable_if<
107  std::is_integral<T>::value && std::is_signed<T>::value,
108  int>::type = 0)
109  : type(SIGNED_INTEGER), signed_integer(_value) {}
110 
111  template <typename T>
113  T _value,
114  typename std::enable_if<
115  std::is_integral<T>::value && std::is_unsigned<T>::value,
116  int>::type = 0)
117  : type(UNSIGNED_INTEGER), unsigned_integer(_value) {}
118 
119  template <typename T>
120  T as() const
121  {
122  switch (type) {
123  case FLOATING:
124  return static_cast<T>(value);
125  case SIGNED_INTEGER:
126  return static_cast<T>(signed_integer);
127  case UNSIGNED_INTEGER:
128  return static_cast<T>(unsigned_integer);
129 
130  // NOTE: By not setting a default we leverage the compiler
131  // errors when the enumeration is augmented to find all
132  // the cases we need to provide.
133  }
134 
135  UNREACHABLE();
136  }
137 
138  enum Type
139  {
143  } type;
144 
145 private:
146  friend struct Value;
147  friend struct Comparator;
148  friend void json(NumberWriter* writer, const Number& number);
149 
150  union {
151  double value;
152  int64_t signed_integer;
154  };
155 };
156 
157 
158 struct Object
159 {
160  Object() = default;
161 
162  Object(std::initializer_list<std::pair<const std::string, Value>> values_)
163  : values(values_) {}
164 
165  // Returns the JSON value (specified by the type) given a "path"
166  // into the structure, for example:
167  //
168  // Result<JSON::Array> array = object.find<JSON::Array>("nested.array[0]");
169  //
170  // Will return 'None' if no field could be found called 'array'
171  // within a field called 'nested' of 'object' (where 'nested' must
172  // also be a JSON object).
173  //
174  // For 'null' field values, this will return 'Some(Null())' when
175  // looking for a matching type ('Null' or 'Value'). If looking for
176  // any other type (e.g. 'String', 'Object', etc), this will return
177  // 'None' as if the field is not present at all.
178  //
179  // Returns an error if a JSON value of the wrong type is found, or
180  // an intermediate JSON value is not an object that we can do a
181  // recursive find on.
182  template <typename T>
183  Result<T> find(const std::string& path) const;
184 
185  // Returns the JSON value by indexing this object with the key. Unlike
186  // find(), the key is not a path into the JSON structure, it is just
187  // a JSON object key name.
188  //
189  // Returns 'None' if there key doesn't exist, or an error if a JSON
190  // value of the wrong type is found.
191  template <typename T>
192  Result<T> at(const std::string& key) const;
193 
194  std::map<std::string, Value> values;
195 };
196 
197 
198 struct Array
199 {
200  Array() = default;
201  Array(std::initializer_list<Value> values_) : values(values_) {}
202 
203  std::vector<Value> values;
204 };
205 
206 
207 struct Boolean
208 {
209  Boolean() : value(false) {}
210  Boolean(bool _value) : value(_value) {}
211  bool value;
212 };
213 
214 
215 // This is a helper so you can say JSON::True() instead of
216 // JSON::Boolean(true).
217 struct True : Boolean
218 {
219  True() : Boolean(true) {}
220 };
221 
222 
223 // This is a helper so you can say JSON::False() instead of
224 // JSON::Boolean(false).
225 struct False : Boolean
226 {
227  False() : Boolean(false) {}
228 };
229 
230 
231 struct Null {};
232 
233 
234 namespace internal {
235 
236 // Null needs to be first so that it is the default value.
237 typedef boost::variant<Null,
238  String,
239  Number,
240  boost::recursive_wrapper<Object>,
241  boost::recursive_wrapper<Array>,
243 
244 } // namespace internal {
245 
246 
248 {
249  // Empty constructor gets the variant default.
250  Value() {}
251 
252  Value(bool value) : internal::Variant(JSON::Boolean(value)) {}
253 
254  Value(char* value) : internal::Variant(JSON::String(value)) {}
255  Value(const char* value) : internal::Variant(JSON::String(value)) {}
256 
257  // Arithmetic types are specifically routed through Number because
258  // there would be ambiguity between JSON::Bool and JSON::Number
259  // otherwise.
260  template <typename T>
262  const T& value,
263  typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0)
264  : internal::Variant(Number(value)) {}
265 
266  // Non-arithmetic types are passed to the default constructor of
267  // Variant.
268  template <typename T>
270  const T& value,
271  typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0)
272  : internal::Variant(value) {}
273 
274  template <typename T>
275  bool is() const;
276 
277  template <typename T>
278  const T& as() const &;
279 
280  template <typename T>
281  T& as() &;
282 
283  template <typename T>
284  T&& as() &&;
285 
286  template <typename T>
287  const T&& as() const &&;
288 
289  // Returns true if and only if 'other' is contained by 'this'.
290  // 'Other' is contained by 'this' if the following conditions are
291  // fulfilled:
292  // 1. If 'other' is a JSON object, then 'this' is also a JSON
293  // object, all keys of 'other' are also present in 'this' and
294  // the value for each key in 'this' also contain the value for
295  // the same key in 'other', i.e. for all keys 'k' in 'other',
296  // 'this[k].contains(other[k])' is true.
297  // 2. If 'other' is a JSON array, 'this' is also a JSON array, the
298  // length of both arrays is the same and each element in 'this'
299  // also contains the element in 'other' at the same position,
300  // i.e. it holds that this.length() == other.length() and
301  // for each i, 0 <= i < this.length,
302  // 'this[i].contains(other[i])'.
303  // 3. For all other types, 'this' is of the same type as 'other' and
304  // 'this == other'.
305  // NOTE: For a given key 'k', if 'this[k] == null' then
306  // 'this.contains(other)' holds if either 'k' is not present in
307  // 'other.keys()' or 'other[k] == null'.
308  // Similarly, if 'other[k] == null', 'this.contains(other)' only if
309  // 'this[k] == null'. This is a consequence of the containment
310  // definition.
311  bool contains(const Value& other) const;
312 
313 private:
314  friend struct Comparator;
315 
316  // A class which follows the visitor pattern and implements the
317  // containment rules described in the documentation of 'contains'.
318  // See 'bool Value::contains(const Value& other) const'.
319  struct ContainmentComparator : public boost::static_visitor<bool>
320  {
321  explicit ContainmentComparator(const Value& _self)
322  : self(_self) {}
323 
324  bool operator()(const Object& other) const;
325  bool operator()(const Array& other) const;
326  bool operator()(const String& other) const;
327  bool operator()(const Number& other) const;
328  bool operator()(const Boolean& other) const;
329  bool operator()(const Null&) const;
330 
331  private:
332  const Value& self;
333  };
334 };
335 
336 
337 template <typename T>
338 bool Value::is() const
339 {
340  const T* t = boost::get<T>(this);
341  return t != nullptr;
342 }
343 
344 
345 template <>
346 inline bool Value::is<Value>() const
347 {
348  return true;
349 }
350 
351 
352 template <typename T>
353 const T& Value::as() const &
354 {
355  return boost::get<T>(*this);
356 }
357 
358 
359 template <typename T>
360 T& Value::as() &
361 {
362  return boost::get<T>(*this);
363 }
364 
365 
366 template <typename T>
367 T&& Value::as() &&
368 {
369  return std::move(boost::get<T>(*this));
370 }
371 
372 
373 template <typename T>
374 const T&& Value::as() const &&
375 {
376  return std::move(boost::get<T>(*this));
377 }
378 
379 
380 template <>
381 inline const Value& Value::as<Value>() const &
382 {
383  return *this;
384 }
385 
386 
387 template <>
388 inline Value& Value::as<Value>() &
389 {
390  return *this;
391 }
392 
393 
394 template <typename T>
395 Result<T> Object::find(const std::string& path) const
396 {
397  const std::vector<std::string> names = strings::split(path, ".", 2);
398 
399  if (names.empty()) {
400  return None();
401  }
402 
403  std::string name = names[0];
404 
405  // Determine if we have an array subscript. If so, save it but
406  // remove it from the name for doing the lookup.
407  Option<size_t> subscript = None();
408  size_t index = name.find('[');
409  if (index != std::string::npos) {
410  // Check for the closing bracket.
411  if (name.at(name.length() - 1) != ']') {
412  return Error("Malformed array subscript, expecting ']'");
413  }
414 
415  // Now remove the closing bracket (last character) and everything
416  // before and including the opening bracket.
417  std::string s = name.substr(index + 1, name.length() - index - 2);
418 
419  // Now numify the subscript.
420  Try<int> i = numify<int>(s);
421 
422  if (i.isError()) {
423  return Error("Failed to numify array subscript '" + s + "'");
424  } else if (i.get() < 0) {
425  return Error("Array subscript '" + s + "' must be >= 0");
426  }
427 
428  subscript = i.get();
429 
430  // And finally remove the array subscript from the name.
431  name = name.substr(0, index);
432  }
433 
434  std::map<std::string, Value>::const_iterator entry = values.find(name);
435 
436  if (entry == values.end()) {
437  return None();
438  }
439 
440  Value value = entry->second;
441 
442  if (subscript.isSome()) {
443  if (value.is<Array>()) {
444  Array array = value.as<Array>();
445  if (subscript.get() >= array.values.size()) {
446  return None();
447  }
448  value = array.values[subscript.get()];
449  } else if (value.is<Null>()) {
450  return None();
451  } else {
452  // TODO(benh): Use a visitor to print out the intermediate type.
453  return Error("Intermediate JSON value not an array");
454  }
455  }
456 
457  if (names.size() == 1) {
458  if (value.is<T>()) {
459  return value.as<T>();
460  } else if (value.is<Null>()) {
461  return None();
462  } else {
463  // TODO(benh): Use a visitor to print out the type found.
464  return Error("Found JSON value of wrong type");
465  }
466  }
467 
468  if (!value.is<Object>()) {
469  // TODO(benh): Use a visitor to print out the intermediate type.
470  return Error("Intermediate JSON value not an object");
471  }
472 
473  return value.as<Object>().find<T>(names[1]);
474 }
475 
476 
477 template <typename T>
478 Result<T> Object::at(const std::string& key) const
479 {
480  if (key.empty()) {
481  return None();
482  }
483 
484  std::map<std::string, Value>::const_iterator entry = values.find(key);
485 
486  if (entry == values.end()) {
487  return None();
488  }
489 
490  Value value = entry->second;
491 
492  if (!value.is<T>()) {
493  // TODO(benh): Use a visitor to print out the type found.
494  return Error("Found JSON value of wrong type");
495  }
496 
497  return value.as<T>();
498 }
499 
500 
501 inline bool Value::contains(const Value& other) const
502 {
503  return boost::apply_visitor(Value::ContainmentComparator(*this), other);
504 }
505 
506 
507 inline bool Value::ContainmentComparator::operator()(const Object& other) const
508 {
509  if (!self.is<Object>()) {
510  return false;
511  }
512 
513  // The empty set is contained in every set.
514  if (other.values.empty()) {
515  return true;
516  }
517 
518  const Object& _self = self.as<Object>();
519 
520  // All entries in 'other' should exists in 'self', which implies
521  // there should be at most as many entries in other as in self.
522  if (other.values.size() > _self.values.size()) {
523  return false;
524  }
525 
526  foreachpair (const std::string& key, const Value& value, other.values) {
527  auto _selfIterator = _self.values.find(key);
528 
529  if (_selfIterator == _self.values.end()) {
530  return false;
531  }
532 
533  if (!_selfIterator->second.contains(value)) {
534  return false;
535  }
536  }
537 
538  return true;
539 }
540 
541 
542 inline bool Value::ContainmentComparator::operator()(const String& other) const
543 {
544  if (!self.is<String>()) {
545  return false;
546  }
547  return self.as<String>().value == other.value;
548 }
549 
550 
551 inline bool Value::ContainmentComparator::operator()(const Number& other) const
552 {
553  if (!self.is<Number>()) {
554  return false;
555  }
556 
557  // NOTE: For the following switch statements, we do not set a default
558  // case in order to leverage the compiler errors when the enumeration
559  // is augmented to find all the cases we need to provide.
560 
561  // NOTE: Using '==' is unsafe for unsigned-signed integer comparisons.
562  // The compiler will automatically cast the signed integer to an
563  // unsigned integer. i.e. If the signed integer was negative, it
564  // might be converted to a large positive number.
565  // Using the '==' operator *is* safe for double-integer comparisons.
566 
567  const Number& number = self.as<Number>();
568  switch (number.type) {
569  case Number::FLOATING:
570  switch (other.type) {
571  case Number::FLOATING:
572  return number.value == other.value;
574  return number.value == other.signed_integer;
576  return number.value == other.unsigned_integer;
577  }
578 
580  switch (other.type) {
581  case Number::FLOATING:
582  return number.signed_integer == other.value;
584  return number.signed_integer == other.signed_integer;
586  // See note above for why this is not a simple '==' expression.
587  return number.signed_integer >= 0 &&
588  number.as<uint64_t>() == other.unsigned_integer;
589  }
590 
592  switch (other.type) {
593  case Number::FLOATING:
594  return number.unsigned_integer == other.value;
596  // See note above for why this is not a simple '==' expression.
597  return other.signed_integer >= 0 &&
598  number.unsigned_integer == other.as<uint64_t>();
600  return number.unsigned_integer == other.unsigned_integer;
601  }
602  }
603 
604  UNREACHABLE();
605 }
606 
607 
608 inline bool Value::ContainmentComparator::operator()(const Array& other) const
609 {
610  if (!self.is<Array>()) {
611  return false;
612  }
613 
614  const Array& _self = self.as<Array>();
615 
616  if (_self.values.size() != other.values.size()) {
617  return false;
618  }
619 
620  for (unsigned i = 0; i < other.values.size(); ++i) {
621  if (!_self.values[i].contains(other.values[i])) {
622  return false;
623  }
624  }
625 
626  return true;
627 }
628 
629 
630 inline bool Value::ContainmentComparator::operator()(const Boolean& other) const
631 {
632  if (!self.is<Boolean>()) {
633  return false;
634  }
635  return self.as<Boolean>().value == other.value;
636 }
637 
638 
639 inline bool Value::ContainmentComparator::operator()(const Null&) const
640 {
641  return self.is<Null>();
642 }
643 
644 
645 struct Comparator : boost::static_visitor<bool>
646 {
647  Comparator(const Value& _value)
648  : value(_value) {}
649 
650  bool operator()(const Object& object) const
651  {
652  if (value.is<Object>()) {
653  return value.as<Object>().values == object.values;
654  }
655  return false;
656  }
657 
658  bool operator()(const String& string) const
659  {
660  if (value.is<String>()) {
661  return value.as<String>().value == string.value;
662  }
663  return false;
664  }
665 
666  bool operator()(const Number& other) const
667  {
668  // Delegate to the containment comparator.
669  // See Value::ContainmentComparator::operator(Number).
670  return Value::ContainmentComparator(value)(other);
671  }
672 
673  bool operator()(const Array& array) const
674  {
675  if (value.is<Array>()) {
676  return value.as<Array>().values == array.values;
677  }
678  return false;
679  }
680 
681  bool operator()(const Boolean& boolean) const
682  {
683  if (value.is<Boolean>()) {
684  return value.as<Boolean>().value == boolean.value;
685  }
686  return false;
687  }
688 
689  bool operator()(const Null&) const
690  {
691  return value.is<Null>();
692  }
693 
694 private:
695  const Value& value;
696 };
697 
698 
699 inline bool operator==(const Value& lhs, const Value& rhs)
700 {
701  return boost::apply_visitor(Comparator(lhs), rhs);
702 }
703 
704 
705 inline bool operator!=(const Value& lhs, const Value& rhs)
706 {
707  return !(lhs == rhs);
708 }
709 
710 // The following are implementation of `json` customization points in order to
711 // use `JSON::*` objects with `jsonify`. This also means that `JSON::*` objects
712 // can be used within other `json` customization points.
713 //
714 // For example, we can use `jsonify` directly like this:
715 //
716 // std::cout << jsonify(JSON::Boolean(true));
717 //
718 // or, for a user-defined class like this:
719 //
720 // struct S
721 // {
722 // std::string name;
723 // JSON::Value payload;
724 // };
725 //
726 // void json(ObjectWriter* writer, const S& s)
727 // {
728 // writer->field("name", s.name);
729 // writer->field("payload", s.payload); // Printing out a `JSON::Value`!
730 // }
731 //
732 // S s{"mpark", JSON::Boolean(true)};
733 // std::cout << jsonify(s); // prints: {"name":"mpark","payload",true}
734 
735 inline void json(BooleanWriter* writer, const Boolean& boolean)
736 {
737  json(writer, boolean.value);
738 }
739 
740 
741 inline void json(StringWriter* writer, const String& string)
742 {
743  json(writer, string.value);
744 }
745 
746 
747 inline void json(NumberWriter* writer, const Number& number)
748 {
749  switch (number.type) {
750  case Number::FLOATING:
751  json(writer, number.value);
752  break;
754  json(writer, number.signed_integer);
755  break;
757  json(writer, number.unsigned_integer);
758  break;
759  }
760 }
761 
762 
763 inline void json(ObjectWriter* writer, const Object& object)
764 {
765  json(writer, object.values);
766 }
767 
768 
769 inline void json(ArrayWriter* writer, const Array& array)
770 {
771  json(writer, array.values);
772 }
773 
774 
775 inline void json(NullWriter*, const Null&)
776 {
777  // Do nothing here since `NullWriter` will always just print `null`.
778 }
779 
780 
781 // Since `JSON::Value` is a `boost::variant`, we don't know what type of writer
782 // is required until we visit it. Therefore, we need an implementation of `json`
783 // which takes a `WriterProxy&&` directly, and constructs the correct writer
784 // after visitation.
785 //
786 // We'd prefer to implement this function similar to the below:
787 //
788 // void json(WriterProxy&& writer, const Value& value)
789 // {
790 // struct {
791 // void operator()(const Number& value) const {
792 // json(std::move(writer), value);
793 // }
794 // void operator()(const String& value) const {
795 // json(std::move(writer), value);
796 // }
797 // /* ... */
798 // } visitor;
799 // boost::apply_visitor(visitor, value);
800 // }
801 //
802 // But, `json` is invoked with `WriterProxy` and something like `JSON::Boolean`.
803 // The version sketched above would be ambiguous with the
804 // `void json(BooleanWriter*, const Boolean&)` version because both overloads
805 // involve a single implicit conversion. The `JSON::Boolean` overload has
806 // a conversion from `WriterProxy` to `BoolWriter*` and the `JSON::Value`
807 // overload has a conversion from `JSON::Boolean` to `JSON::Value`. In order to
808 // prefer the overloads such as the one for `JSON::Boolean`, we disallow the
809 // implicit conversion to `JSON::Value` by declaring as a template.
810 //
811 // TODO(mpark): Properly introduce a notion of deferred choice of writers.
812 // For example, when trying to print a `variant<int, string>` as the value,
813 // we could take something like a `Writer<Number, String>` which can be turned
814 // into either a `NumberWriter*` or `StringWriter*`.
815 template <
816  typename T,
817  typename std::enable_if<std::is_same<T, Value>::value, int>::type = 0>
818 void json(WriterProxy&& writer, const T& value)
819 {
820  struct
821  {
822  using result_type = void;
823 
824  void operator()(const Boolean& value) const
825  {
826  json(std::move(writer_), value);
827  }
828  void operator()(const String& value) const
829  {
830  json(std::move(writer_), value);
831  }
832  void operator()(const Number& value) const
833  {
834  json(std::move(writer_), value);
835  }
836  void operator()(const Object& value) const
837  {
838  json(std::move(writer_), value);
839  }
840  void operator()(const Array& value) const
841  {
842  json(std::move(writer_), value);
843  }
844  void operator()(const Null& value) const
845  {
846  json(std::move(writer_), value);
847  }
848 
849  WriterProxy&& writer_;
850  } visitor{std::move(writer)};
851  boost::apply_visitor(visitor, value);
852 }
853 
854 
855 inline std::ostream& operator<<(std::ostream& stream, const Boolean& boolean)
856 {
857  return stream << jsonify(boolean);
858 }
859 
860 
861 inline std::ostream& operator<<(std::ostream& stream, const String& string)
862 {
863  return stream << jsonify(string);
864 }
865 
866 
867 inline std::ostream& operator<<(std::ostream& stream, const Number& number)
868 {
869  return stream << jsonify(number);
870 }
871 
872 
873 inline std::ostream& operator<<(std::ostream& stream, const Object& object)
874 {
875  return stream << jsonify(object);
876 }
877 
878 
879 inline std::ostream& operator<<(std::ostream& stream, const Array& array)
880 {
881  return stream << jsonify(array);
882 }
883 
884 
885 inline std::ostream& operator<<(std::ostream& stream, const Null& null)
886 {
887  return stream << jsonify(null);
888 }
889 
890 namespace internal {
891 
892 // "Depth" is counted downwards to stay closer to the analogous
893 // implementation in PicoJSON.
894 constexpr size_t STOUT_JSON_MAX_DEPTH = 200;
895 
896 // Our implementation of picojson's parsing context that allows
897 // us to parse directly into our JSON::Value.
898 //
899 // https://github.com/kazuho/picojson/blob/v1.3.0/picojson.h#L820-L870
901 public:
902  ParseContext(Value* _value, size_t _depth = STOUT_JSON_MAX_DEPTH)
903  : value(_value), depth(_depth) {}
904 
905  ParseContext(const ParseContext&) = delete;
906  ParseContext &operator=(const ParseContext&) = delete;
907 
908  bool set_null() { *value = Null(); return true; }
909  bool set_bool(bool b) { *value = Boolean(b); return true; }
910  bool set_int64(int64_t i) { *value = Number(i); return true; }
911 
912  bool set_number(double f) {
913  // We take a trip through picojson::value here because it
914  // is where the validation takes place (i.e. it throws):
915  // https://github.com/kazuho/picojson/issues/94
916  // https://github.com/kazuho/picojson/blob/v1.3.0/picojson.h#L195-L208
917  picojson::value v(f);
918  *value = Number(v.get<double>());
919  return true;
920  }
921 
922  template <typename Iter>
923  bool parse_string(picojson::input<Iter>& in) {
924  *value = String();
925  return picojson::_parse_string(value->as<String>().value, in);
926  }
927 
929  if (depth <= 0) {
930  return false;
931  }
932  --depth;
933  *value = Array();
934  return true;
935  }
936 
937  template <typename Iter>
938  bool parse_array_item(picojson::input<Iter>& in, size_t) {
939  Array& array = value->as<Array>();
940  array.values.push_back(Value());
941  ParseContext context(&array.values.back(), depth);
942  return picojson::_parse(context, in);
943  }
944 
945  bool parse_array_stop(size_t) {
946  ++depth;
947  return true;
948  }
949 
951  if (depth <= 0) {
952  return false;
953  }
954  --depth;
955  *value = Object();
956  return true;
957  }
958 
959  template <typename Iter>
960  bool parse_object_item(picojson::input<Iter>& in, const std::string& key) {
961  Object& object = value->as<Object>();
962  ParseContext context(&object.values[key], depth);
963  return picojson::_parse(context, in);
964  }
965 
967  ++depth;
968  return true;
969  }
970 
972  size_t depth;
973 };
974 
975 } // namespace internal {
976 
977 
978 inline Try<Value> parse(const std::string& s)
979 {
980  const char* parseBegin = s.c_str();
981  Value value;
982  std::string error;
983 
984  // Because PicoJson supports repeated parsing of multiple objects/arrays in a
985  // stream, it will quietly ignore trailing non-whitespace characters. We would
986  // rather throw an error, however, so use `last_char` to check for this.
987  //
988  // TODO(alexr): Address cases when `s` is empty or consists only of whitespace
989  // characters.
990  const char* lastVisibleChar =
991  parseBegin + s.find_last_not_of(strings::WHITESPACE);
992 
993  // Parse the string, returning a pointer to the character immediately
994  // following the last one parsed. Convert exceptions to `Error`s.
995  //
996  // TODO(alexr): Remove `try-catch` wrapper once picojson stops throwing
997  // on parsing, see https://github.com/kazuho/picojson/issues/94
998  const char* parseEnd;
999  try {
1001  parseEnd =
1002  picojson::_parse(context, parseBegin, parseBegin + s.size(), &error);
1003  } catch (const std::overflow_error&) {
1004  return Error("Value out of range");
1005  } catch (...) {
1006  return Error("Unknown JSON parse error");
1007  }
1008 
1009  if (!error.empty()) {
1010  return Error(error);
1011  } else if (parseEnd != lastVisibleChar + 1) {
1012  return Error(
1013  "Parsed JSON included non-whitespace trailing characters: "
1014  + s.substr(parseEnd - parseBegin, lastVisibleChar + 1 - parseEnd));
1015  }
1016 
1017  // TODO(bmahler): Newer compilers (clang-3.9 and gcc-5.1) can
1018  // perform a move into the resultant Try with optimization on.
1019  // Consider removing the `std::move` when we require these
1020  // compilers.
1021  return std::move(value);
1022 }
1023 
1024 
1025 template <typename T>
1026 Try<T> parse(const std::string& s)
1027 {
1028  Try<Value> value = parse(s);
1029 
1030  if (value.isError()) {
1031  return Error(value.error());
1032  }
1033 
1034  if (!value->is<T>()) {
1035  return Error("Unexpected JSON type parsed");
1036  }
1037 
1038  return std::move(value->as<T>());
1039 }
1040 
1041 
1042 template <>
1043 inline Try<Value> parse(const std::string& s)
1044 {
1045  return parse(s);
1046 }
1047 
1048 } // namespace JSON {
1049 
1050 #endif // __STOUT_JSON__
T as() const
Definition: json.hpp:120
bool parse_array_item(picojson::input< Iter > &in, size_t)
Definition: json.hpp:938
Definition: json.hpp:140
Definition: path.hpp:29
int64_t signed_integer
Definition: json.hpp:152
Value(const T &value, typename std::enable_if< std::is_arithmetic< T >::value, int >::type=0)
Definition: json.hpp:261
constexpr size_t STOUT_JSON_MAX_DEPTH
Definition: json.hpp:894
bool operator()(const Null &) const
Definition: json.hpp:689
bool value
Definition: json.hpp:211
Definition: errorbase.hpp:36
bool operator()(const Number &other) const
Definition: json.hpp:666
F && f
Definition: defer.hpp:270
T & get()&
Definition: try.hpp:80
Boolean()
Definition: json.hpp:209
Value(char *value)
Definition: json.hpp:254
Definition: json.hpp:231
Definition: check.hpp:33
Definition: json.hpp:207
Definition: json.hpp:225
Definition: jsonify.hpp:364
Definition: jsonify.hpp:137
bool parse_string(picojson::input< Iter > &in)
Definition: json.hpp:923
Definition: json.hpp:141
Type
Definition: json.hpp:138
Definition: json.hpp:198
Definition: json.hpp:142
std::map< std::string, Value > values
Definition: json.hpp:194
Definition: jsonify.hpp:255
Definition: check.hpp:30
Value()
Definition: json.hpp:250
bool isSome() const
Definition: option.hpp:116
Definition: json.hpp:158
size_t depth
Definition: json.hpp:972
Value(bool value)
Definition: json.hpp:252
bool is() const
Definition: json.hpp:338
bool set_int64(int64_t i)
Definition: json.hpp:910
Result< T > at(const std::string &key) const
Definition: json.hpp:478
double value
Definition: json.hpp:151
void json(BooleanWriter *writer, const Boolean &boolean)
Definition: json.hpp:735
Number(T _value, typename std::enable_if< std::is_integral< T >::value &&std::is_signed< T >::value, int >::type=0)
Definition: json.hpp:104
Value * value
Definition: json.hpp:971
bool parse_array_stop(size_t)
Definition: json.hpp:945
Definition: json.hpp:217
String(const char *_value)
Definition: json.hpp:82
Array(std::initializer_list< Value > values_)
Definition: json.hpp:201
Number(T _value, typename std::enable_if< std::is_floating_point< T >::value, int >::type=0)
Definition: json.hpp:98
bool operator!=(const Value &lhs, const Value &rhs)
Definition: json.hpp:705
String()
Definition: json.hpp:81
const T & as() const &
Definition: json.hpp:353
Definition: variant.hpp:47
const std::string WHITESPACE
Definition: strings.hpp:30
Definition: jsonify.hpp:326
const T & get() const &
Definition: option.hpp:119
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
String(const std::string &_value)
Definition: json.hpp:83
Try< Value > parse(const std::string &s)
Returns the OCI v1 descriptor, image index, image manifest and image configuration from the given str...
Definition: json.hpp:978
std::vector< Value > values
Definition: json.hpp:203
Definition: json.hpp:645
Definition: jsonify.hpp:161
std::ostream & operator<<(std::ostream &stream, const Boolean &boolean)
Definition: json.hpp:855
static Try error(const E &e)
Definition: try.hpp:43
bool operator()(const Object &object) const
Definition: json.hpp:650
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:702
#define UNREACHABLE()
Definition: unreachable.hpp:22
Definition: json.hpp:900
Value(const T &value, typename std::enable_if<!std::is_arithmetic< T >::value, int >::type=0)
Definition: json.hpp:269
Definition: json.hpp:247
bool operator==(const Value &lhs, const Value &rhs)
Definition: json.hpp:699
Definition: none.hpp:27
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
bool parse_object_start()
Definition: json.hpp:950
bool parse_array_start()
Definition: json.hpp:928
Number(T _value, typename std::enable_if< std::is_integral< T >::value &&std::is_unsigned< T >::value, int >::type=0)
Definition: json.hpp:112
std::string error(const std::string &msg, uint32_t code)
Definition: json.hpp:79
bool contains(const Value &other) const
Definition: json.hpp:501
Value(const char *value)
Definition: json.hpp:255
False()
Definition: json.hpp:227
Try< uint32_t > type(const std::string &path)
Comparator(const Value &_value)
Definition: json.hpp:647
std::vector< std::string > split(const std::string &s, const std::string &delims, const Option< size_t > &maxTokens=None())
Definition: strings.hpp:184
Definition: json.hpp:93
bool operator()(const Array &array) const
Definition: json.hpp:673
bool set_bool(bool b)
Definition: json.hpp:909
bool operator()(const Boolean &boolean) const
Definition: json.hpp:681
bool operator()(const String &string) const
Definition: json.hpp:658
boost::variant< Null, String, Number, boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, Boolean > Variant
Definition: json.hpp:242
Number()
Definition: json.hpp:95
bool parse_object_item(picojson::input< Iter > &in, const std::string &key)
Definition: json.hpp:960
uint64_t unsigned_integer
Definition: json.hpp:153
bool set_null()
Definition: json.hpp:908
enum JSON::Number::Type type
Result< T > find(const std::string &path) const
Definition: json.hpp:395
ParseContext(Value *_value, size_t _depth=STOUT_JSON_MAX_DEPTH)
Definition: json.hpp:902
constexpr const char * name
Definition: shell.hpp:41
Definition: json.hpp:51
bool contains(const Resource &left, const Resource &right)
Boolean(bool _value)
Definition: json.hpp:210
Definition: jsonify.hpp:557
bool parse_object_stop()
Definition: json.hpp:966
std::string value
Definition: json.hpp:84
True()
Definition: json.hpp:219
Try< std::list< std::string > > find(const std::string &directory, const std::string &pattern)
Definition: find.hpp:37
Definition: jsonify.hpp:296
Object(std::initializer_list< std::pair< const std::string, Value >> values_)
Definition: json.hpp:162
bool set_number(double f)
Definition: json.hpp:912