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