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 // Only Object and Array require recursive_wrapper, not sure
233 // if there is a reason to wrap the others or not.
234 // Null needs to be first so that it is the default value.
235 typedef boost::variant<boost::recursive_wrapper<Null>,
236  boost::recursive_wrapper<String>,
237  boost::recursive_wrapper<Number>,
238  boost::recursive_wrapper<Object>,
239  boost::recursive_wrapper<Array>,
240  boost::recursive_wrapper<Boolean>> Variant;
241 
242 } // namespace internal {
243 
244 
246 {
247  // Empty constructor gets the variant default.
248  Value() {}
249 
250  Value(bool value) : internal::Variant(JSON::Boolean(value)) {}
251 
252  Value(char* value) : internal::Variant(JSON::String(value)) {}
253  Value(const char* value) : internal::Variant(JSON::String(value)) {}
254 
255  // Arithmetic types are specifically routed through Number because
256  // there would be ambiguity between JSON::Bool and JSON::Number
257  // otherwise.
258  template <typename T>
260  const T& value,
261  typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0)
262  : internal::Variant(Number(value)) {}
263 
264  // Non-arithmetic types are passed to the default constructor of
265  // Variant.
266  template <typename T>
268  const T& value,
269  typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0)
270  : internal::Variant(value) {}
271 
272  template <typename T>
273  bool is() const;
274 
275  template <typename T>
276  const T& as() const;
277 
278  // Returns true if and only if 'other' is contained by 'this'.
279  // 'Other' is contained by 'this' if the following conditions are
280  // fulfilled:
281  // 1. If 'other' is a JSON object, then 'this' is also a JSON
282  // object, all keys of 'other' are also present in 'this' and
283  // the value for each key in 'this' also contain the value for
284  // the same key in 'other', i.e. for all keys 'k' in 'other',
285  // 'this[k].contains(other[k])' is true.
286  // 2. If 'other' is a JSON array, 'this' is also a JSON array, the
287  // length of both arrays is the same and each element in 'this'
288  // also contains the element in 'other' at the same position,
289  // i.e. it holds that this.length() == other.length() and
290  // for each i, 0 <= i < this.length,
291  // 'this[i].contains(other[i])'.
292  // 3. For all other types, 'this' is of the same type as 'other' and
293  // 'this == other'.
294  // NOTE: For a given key 'k', if 'this[k] == null' then
295  // 'this.contains(other)' holds if either 'k' is not present in
296  // 'other.keys()' or 'other[k] == null'.
297  // Similarly, if 'other[k] == null', 'this.contains(other)' only if
298  // 'this[k] == null'. This is a consequence of the containment
299  // definition.
300  bool contains(const Value& other) const;
301 
302 private:
303  friend struct Comparator;
304 
305  // A class which follows the visitor pattern and implements the
306  // containment rules described in the documentation of 'contains'.
307  // See 'bool Value::contains(const Value& other) const'.
308  struct ContainmentComparator : public boost::static_visitor<bool>
309  {
310  explicit ContainmentComparator(const Value& _self)
311  : self(_self) {}
312 
313  bool operator()(const Object& other) const;
314  bool operator()(const Array& other) const;
315  bool operator()(const String& other) const;
316  bool operator()(const Number& other) const;
317  bool operator()(const Boolean& other) const;
318  bool operator()(const Null&) const;
319 
320  private:
321  const Value& self;
322  };
323 };
324 
325 
326 template <typename T>
327 bool Value::is() const
328 {
329  const T* t = boost::get<T>(this);
330  return t != nullptr;
331 }
332 
333 
334 template <>
335 inline bool Value::is<Value>() const
336 {
337  return true;
338 }
339 
340 
341 template <typename T>
342 const T& Value::as() const
343 {
344  return *CHECK_NOTNULL(boost::get<T>(this));
345 }
346 
347 
348 template <>
349 inline const Value& Value::as<Value>() const
350 {
351  return *this;
352 }
353 
354 
355 template <typename T>
356 Result<T> Object::find(const std::string& path) const
357 {
358  const std::vector<std::string> names = strings::split(path, ".", 2);
359 
360  if (names.empty()) {
361  return None();
362  }
363 
364  std::string name = names[0];
365 
366  // Determine if we have an array subscript. If so, save it but
367  // remove it from the name for doing the lookup.
368  Option<size_t> subscript = None();
369  size_t index = name.find('[');
370  if (index != std::string::npos) {
371  // Check for the closing bracket.
372  if (name.at(name.length() - 1) != ']') {
373  return Error("Malformed array subscript, expecting ']'");
374  }
375 
376  // Now remove the closing bracket (last character) and everything
377  // before and including the opening bracket.
378  std::string s = name.substr(index + 1, name.length() - index - 2);
379 
380  // Now numify the subscript.
381  Try<int> i = numify<int>(s);
382 
383  if (i.isError()) {
384  return Error("Failed to numify array subscript '" + s + "'");
385  } else if (i.get() < 0) {
386  return Error("Array subscript '" + s + "' must be >= 0");
387  }
388 
389  subscript = i.get();
390 
391  // And finally remove the array subscript from the name.
392  name = name.substr(0, index);
393  }
394 
395  std::map<std::string, Value>::const_iterator entry = values.find(name);
396 
397  if (entry == values.end()) {
398  return None();
399  }
400 
401  Value value = entry->second;
402 
403  if (subscript.isSome()) {
404  if (value.is<Array>()) {
405  Array array = value.as<Array>();
406  if (subscript.get() >= array.values.size()) {
407  return None();
408  }
409  value = array.values[subscript.get()];
410  } else if (value.is<Null>()) {
411  return None();
412  } else {
413  // TODO(benh): Use a visitor to print out the intermediate type.
414  return Error("Intermediate JSON value not an array");
415  }
416  }
417 
418  if (names.size() == 1) {
419  if (value.is<T>()) {
420  return value.as<T>();
421  } else if (value.is<Null>()) {
422  return None();
423  } else {
424  // TODO(benh): Use a visitor to print out the type found.
425  return Error("Found JSON value of wrong type");
426  }
427  }
428 
429  if (!value.is<Object>()) {
430  // TODO(benh): Use a visitor to print out the intermediate type.
431  return Error("Intermediate JSON value not an object");
432  }
433 
434  return value.as<Object>().find<T>(names[1]);
435 }
436 
437 
438 template <typename T>
439 Result<T> Object::at(const std::string& key) const
440 {
441  if (key.empty()) {
442  return None();
443  }
444 
445  std::map<std::string, Value>::const_iterator entry = values.find(key);
446 
447  if (entry == values.end()) {
448  return None();
449  }
450 
451  Value value = entry->second;
452 
453  if (!value.is<T>()) {
454  // TODO(benh): Use a visitor to print out the type found.
455  return Error("Found JSON value of wrong type");
456  }
457 
458  return value.as<T>();
459 }
460 
461 
462 inline bool Value::contains(const Value& other) const
463 {
464  return boost::apply_visitor(Value::ContainmentComparator(*this), other);
465 }
466 
467 
468 inline bool Value::ContainmentComparator::operator()(const Object& other) const
469 {
470  if (!self.is<Object>()) {
471  return false;
472  }
473 
474  // The empty set is contained in every set.
475  if (other.values.empty()) {
476  return true;
477  }
478 
479  const Object& _self = self.as<Object>();
480 
481  // All entries in 'other' should exists in 'self', which implies
482  // there should be at most as many entries in other as in self.
483  if (other.values.size() > _self.values.size()) {
484  return false;
485  }
486 
487  foreachpair (const std::string& key, const Value& value, other.values) {
488  auto _selfIterator = _self.values.find(key);
489 
490  if (_selfIterator == _self.values.end()) {
491  return false;
492  }
493 
494  if (!_selfIterator->second.contains(value)) {
495  return false;
496  }
497  }
498 
499  return true;
500 }
501 
502 
503 inline bool Value::ContainmentComparator::operator()(const String& other) const
504 {
505  if (!self.is<String>()) {
506  return false;
507  }
508  return self.as<String>().value == other.value;
509 }
510 
511 
512 inline bool Value::ContainmentComparator::operator()(const Number& other) const
513 {
514  if (!self.is<Number>()) {
515  return false;
516  }
517 
518  // NOTE: For the following switch statements, we do not set a default
519  // case in order to leverage the compiler errors when the enumeration
520  // is augmented to find all the cases we need to provide.
521 
522  // NOTE: Using '==' is unsafe for unsigned-signed integer comparisons.
523  // The compiler will automatically cast the signed integer to an
524  // unsigned integer. i.e. If the signed integer was negative, it
525  // might be converted to a large positive number.
526  // Using the '==' operator *is* safe for double-integer comparisons.
527 
528  const Number& number = self.as<Number>();
529  switch (number.type) {
530  case Number::FLOATING:
531  switch (other.type) {
532  case Number::FLOATING:
533  return number.value == other.value;
535  return number.value == other.signed_integer;
537  return number.value == other.unsigned_integer;
538  }
539 
541  switch (other.type) {
542  case Number::FLOATING:
543  return number.signed_integer == other.value;
545  return number.signed_integer == other.signed_integer;
547  // See note above for why this is not a simple '==' expression.
548  return number.signed_integer >= 0 &&
549  number.as<uint64_t>() == other.unsigned_integer;
550  }
551 
553  switch (other.type) {
554  case Number::FLOATING:
555  return number.unsigned_integer == other.value;
557  // See note above for why this is not a simple '==' expression.
558  return other.signed_integer >= 0 &&
559  number.unsigned_integer == other.as<uint64_t>();
561  return number.unsigned_integer == other.unsigned_integer;
562  }
563  }
564 
565  UNREACHABLE();
566 }
567 
568 
569 inline bool Value::ContainmentComparator::operator()(const Array& other) const
570 {
571  if (!self.is<Array>()) {
572  return false;
573  }
574 
575  const Array& _self = self.as<Array>();
576 
577  if (_self.values.size() != other.values.size()) {
578  return false;
579  }
580 
581  for (unsigned i = 0; i < other.values.size(); ++i) {
582  if (!_self.values[i].contains(other.values[i])) {
583  return false;
584  }
585  }
586 
587  return true;
588 }
589 
590 
591 inline bool Value::ContainmentComparator::operator()(const Boolean& other) const
592 {
593  if (!self.is<Boolean>()) {
594  return false;
595  }
596  return self.as<Boolean>().value == other.value;
597 }
598 
599 
600 inline bool Value::ContainmentComparator::operator()(const Null&) const
601 {
602  return self.is<Null>();
603 }
604 
605 
606 struct Comparator : boost::static_visitor<bool>
607 {
608  Comparator(const Value& _value)
609  : value(_value) {}
610 
611  bool operator()(const Object& object) const
612  {
613  if (value.is<Object>()) {
614  return value.as<Object>().values == object.values;
615  }
616  return false;
617  }
618 
619  bool operator()(const String& string) const
620  {
621  if (value.is<String>()) {
622  return value.as<String>().value == string.value;
623  }
624  return false;
625  }
626 
627  bool operator()(const Number& other) const
628  {
629  // Delegate to the containment comparator.
630  // See Value::ContainmentComparator::operator(Number).
631  return Value::ContainmentComparator(value)(other);
632  }
633 
634  bool operator()(const Array& array) const
635  {
636  if (value.is<Array>()) {
637  return value.as<Array>().values == array.values;
638  }
639  return false;
640  }
641 
642  bool operator()(const Boolean& boolean) const
643  {
644  if (value.is<Boolean>()) {
645  return value.as<Boolean>().value == boolean.value;
646  }
647  return false;
648  }
649 
650  bool operator()(const Null&) const
651  {
652  return value.is<Null>();
653  }
654 
655 private:
656  const Value& value;
657 };
658 
659 
660 inline bool operator==(const Value& lhs, const Value& rhs)
661 {
662  return boost::apply_visitor(Comparator(lhs), rhs);
663 }
664 
665 
666 inline bool operator!=(const Value& lhs, const Value& rhs)
667 {
668  return !(lhs == rhs);
669 }
670 
671 // The following are implementation of `json` customization points in order to
672 // use `JSON::*` objects with `jsonify`. This also means that `JSON::*` objects
673 // can be used within other `json` customization points.
674 //
675 // For example, we can use `jsonify` directly like this:
676 //
677 // std::cout << jsonify(JSON::Boolean(true));
678 //
679 // or, for a user-defined class like this:
680 //
681 // struct S
682 // {
683 // std::string name;
684 // JSON::Value payload;
685 // };
686 //
687 // void json(ObjectWriter* writer, const S& s)
688 // {
689 // writer->field("name", s.name);
690 // writer->field("payload", s.payload); // Printing out a `JSON::Value`!
691 // }
692 //
693 // S s{"mpark", JSON::Boolean(true)};
694 // std::cout << jsonify(s); // prints: {"name":"mpark","payload",true}
695 
696 inline void json(BooleanWriter* writer, const Boolean& boolean)
697 {
698  json(writer, boolean.value);
699 }
700 
701 
702 inline void json(StringWriter* writer, const String& string)
703 {
704  json(writer, string.value);
705 }
706 
707 
708 inline void json(NumberWriter* writer, const Number& number)
709 {
710  switch (number.type) {
711  case Number::FLOATING:
712  json(writer, number.value);
713  break;
715  json(writer, number.signed_integer);
716  break;
718  json(writer, number.unsigned_integer);
719  break;
720  }
721 }
722 
723 
724 inline void json(ObjectWriter* writer, const Object& object)
725 {
726  json(writer, object.values);
727 }
728 
729 
730 inline void json(ArrayWriter* writer, const Array& array)
731 {
732  json(writer, array.values);
733 }
734 
735 
736 inline void json(NullWriter*, const Null&)
737 {
738  // Do nothing here since `NullWriter` will always just print `null`.
739 }
740 
741 
742 // Since `JSON::Value` is a `boost::variant`, we don't know what type of writer
743 // is required until we visit it. Therefore, we need an implementation of `json`
744 // which takes a `WriterProxy&&` directly, and constructs the correct writer
745 // after visitation.
746 //
747 // We'd prefer to implement this function similar to the below:
748 //
749 // void json(WriterProxy&& writer, const Value& value)
750 // {
751 // struct {
752 // void operator()(const Number& value) const {
753 // json(std::move(writer), value);
754 // }
755 // void operator()(const String& value) const {
756 // json(std::move(writer), value);
757 // }
758 // /* ... */
759 // } visitor;
760 // boost::apply_visitor(visitor, value);
761 // }
762 //
763 // But, `json` is invoked with `WriterProxy` and something like `JSON::Boolean`.
764 // The version sketched above would be ambiguous with the
765 // `void json(BooleanWriter*, const Boolean&)` version because both overloads
766 // involve a single implicit conversion. The `JSON::Boolean` overload has
767 // a conversion from `WriterProxy` to `BoolWriter*` and the `JSON::Value`
768 // overload has a conversion from `JSON::Boolean` to `JSON::Value`. In order to
769 // prefer the overloads such as the one for `JSON::Boolean`, we disallow the
770 // implicit conversion to `JSON::Value` by declaring as a template.
771 //
772 // TODO(mpark): Properly introduce a notion of deferred choice of writers.
773 // For example, when trying to print a `variant<int, string>` as the value,
774 // we could take something like a `Writer<Number, String>` which can be turned
775 // into either a `NumberWriter*` or `StringWriter*`.
776 template <
777  typename T,
778  typename std::enable_if<std::is_same<T, Value>::value, int>::type = 0>
779 void json(WriterProxy&& writer, const T& value)
780 {
781  struct
782  {
783  using result_type = void;
784 
785  void operator()(const Boolean& value) const
786  {
787  json(std::move(writer_), value);
788  }
789  void operator()(const String& value) const
790  {
791  json(std::move(writer_), value);
792  }
793  void operator()(const Number& value) const
794  {
795  json(std::move(writer_), value);
796  }
797  void operator()(const Object& value) const
798  {
799  json(std::move(writer_), value);
800  }
801  void operator()(const Array& value) const
802  {
803  json(std::move(writer_), value);
804  }
805  void operator()(const Null& value) const
806  {
807  json(std::move(writer_), value);
808  }
809 
810  WriterProxy&& writer_;
811  } visitor{std::move(writer)};
812  boost::apply_visitor(visitor, value);
813 }
814 
815 
816 inline std::ostream& operator<<(std::ostream& stream, const Boolean& boolean)
817 {
818  return stream << jsonify(boolean);
819 }
820 
821 
822 inline std::ostream& operator<<(std::ostream& stream, const String& string)
823 {
824  return stream << jsonify(string);
825 }
826 
827 
828 inline std::ostream& operator<<(std::ostream& stream, const Number& number)
829 {
830  return stream << jsonify(number);
831 }
832 
833 
834 inline std::ostream& operator<<(std::ostream& stream, const Object& object)
835 {
836  return stream << jsonify(object);
837 }
838 
839 
840 inline std::ostream& operator<<(std::ostream& stream, const Array& array)
841 {
842  return stream << jsonify(array);
843 }
844 
845 
846 inline std::ostream& operator<<(std::ostream& stream, const Null& null)
847 {
848  return stream << jsonify(null);
849 }
850 
851 namespace internal {
852 
853 inline Value convert(const picojson::value& value)
854 {
855  if (value.is<picojson::null>()) {
856  return Null();
857  } else if (value.is<bool>()) {
858  return Boolean(value.get<bool>());
859  } else if (value.is<picojson::value::object>()) {
860  Object object;
861  foreachpair (const std::string& name,
862  const picojson::value& v,
863  value.get<picojson::value::object>()) {
864  object.values[name] = convert(v);
865  }
866  return object;
867  } else if (value.is<picojson::value::array>()) {
868  Array array;
869  foreach (const picojson::value& v, value.get<picojson::value::array>()) {
870  array.values.push_back(convert(v));
871  }
872  return array;
873  } else if (value.is<int64_t>()) {
874  return Number(value.get<int64_t>());
875  } else if (value.is<double>()) {
876  return Number(value.get<double>());
877  } else if (value.is<std::string>()) {
878  return String(value.get<std::string>());
879  }
880  return Null();
881 }
882 
883 } // namespace internal {
884 
885 
886 inline Try<Value> parse(const std::string& s)
887 {
888  const char* parseBegin = s.c_str();
889  picojson::value value;
890  std::string error;
891 
892  // Because PicoJson supports repeated parsing of multiple objects/arrays in a
893  // stream, it will quietly ignore trailing non-whitespace characters. We would
894  // rather throw an error, however, so use `last_char` to check for this.
895  const char* lastVisibleChar =
896  parseBegin + s.find_last_not_of(strings::WHITESPACE);
897 
898  // Parse the string, returning a pointer to the character
899  // immediately following the last one parsed.
900  const char* parseEnd =
901  picojson::parse(value, parseBegin, parseBegin + s.size(), &error);
902 
903  if (!error.empty()) {
904  return Error(error);
905  } else if (parseEnd != lastVisibleChar + 1) {
906  return Error(
907  "Parsed JSON included non-whitespace trailing characters: "
908  + s.substr(parseEnd - parseBegin, lastVisibleChar + 1 - parseEnd));
909  }
910 
911  return internal::convert(value);
912 }
913 
914 
915 template <typename T>
916 Try<T> parse(const std::string& s)
917 {
918  Try<Value> value = parse(s);
919 
920  if (value.isError()) {
921  return Error(value.error());
922  }
923 
924  if (!value.get().is<T>()) {
925  return Error("Unexpected JSON type parsed");
926  }
927 
928  return value.get().as<T>();
929 }
930 
931 
932 template <>
933 inline Try<Value> parse(const std::string& s)
934 {
935  return parse(s);
936 }
937 
938 } // namespace JSON {
939 
940 #endif // __STOUT_JSON__
T as() const
Definition: json.hpp:116
Definition: json.hpp:136
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:259
const T & as() const
Definition: json.hpp:342
bool operator()(const Null &) const
Definition: json.hpp:650
bool value
Definition: json.hpp:207
Definition: errorbase.hpp:35
bool operator()(const Number &other) const
Definition: json.hpp:627
Boolean()
Definition: json.hpp:205
friend void json(NumberWriter *writer, const Number &number)
Definition: json.hpp:708
Value(char *value)
Definition: json.hpp:252
Definition: json.hpp:227
Definition: try.hpp:34
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: result.hpp:40
Value()
Definition: json.hpp:248
bool isSome() const
Definition: option.hpp:115
Definition: json.hpp:154
Value(bool value)
Definition: json.hpp:250
bool is() const
Definition: json.hpp:327
Result< T > at(const std::string &key) const
Definition: json.hpp:439
double value
Definition: json.hpp:147
void json(BooleanWriter *writer, const Boolean &boolean)
Definition: json.hpp:696
Number(T _value, typename std::enable_if< std::is_integral< T >::value &&std::is_signed< T >::value, int >::type=0)
Definition: json.hpp:100
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:666
Array()=default
String()
Definition: json.hpp:77
Definition: variant.hpp:47
const std::string WHITESPACE
Definition: strings.hpp:29
Try< ImageManifest > parse(const std::string &value)
Definition: parse.hpp:36
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:886
std::vector< Value > values
Definition: json.hpp:199
Definition: json.hpp:606
Definition: jsonify.hpp:202
boost::variant< boost::recursive_wrapper< Null >, boost::recursive_wrapper< String >, boost::recursive_wrapper< Number >, boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, boost::recursive_wrapper< Boolean > > Variant
Definition: json.hpp:240
std::ostream & operator<<(std::ostream &stream, const Boolean &boolean)
Definition: json.hpp:816
static Try error(const E &e)
Definition: try.hpp:42
bool operator()(const Object &object) const
Definition: json.hpp:611
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:267
Definition: json.hpp:245
Object()=default
bool operator==(const Value &lhs, const Value &rhs)
Definition: json.hpp:660
Definition: none.hpp:27
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)
Definition: json.hpp:75
bool contains(const Value &other) const
Definition: json.hpp:462
Value(const char *value)
Definition: json.hpp:253
False()
Definition: json.hpp:223
Try< uint32_t > type(const std::string &path)
Comparator(const Value &_value)
Definition: json.hpp:608
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:634
bool operator()(const Boolean &boolean) const
Definition: json.hpp:642
bool operator()(const String &string) const
Definition: json.hpp:619
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:356
Value convert(const picojson::value &value)
Definition: json.hpp:853
const T & get() const
Definition: try.hpp:73
constexpr const char * name
Definition: shell.hpp:41
Boolean(bool _value)
Definition: json.hpp:206
Definition: jsonify.hpp:642
std::string value
Definition: json.hpp:80
True()
Definition: json.hpp:215
Definition: jsonify.hpp:384
Object(std::initializer_list< std::pair< const std::string, Value >> values_)
Definition: json.hpp:158