libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
json.h
Go to the documentation of this file.
1
9#pragma once
10
11
12#include <promeki/config.h>
13#if PROMEKI_ENABLE_CORE
14#include <functional>
15#include <utility>
16#include <promeki/function.h>
17#include <promeki/namespace.h>
18#include <promeki/string.h>
19#include <promeki/list.h>
20#include <promeki/sharedptr.h>
21#include <promeki/datatype.h>
22#include <promeki/uuid.h>
23#include <nlohmann/json.hpp>
24
25PROMEKI_NAMESPACE_BEGIN
26
27class Variant;
28class VariantList;
29class VariantMap;
30class DataStream;
31
32class JsonValue;
33class JsonObject;
34class JsonArray;
35
47struct JsonData {
48 PROMEKI_SHARED_FINAL(JsonData)
49 nlohmann::json j;
50
51 JsonData() = default;
52 explicit JsonData(nlohmann::json v) : j(std::move(v)) {}
53};
54
89class JsonValue {
90 public:
92 enum Type {
93 Null,
94 Bool,
95 Double,
96 String,
97 Array,
98 Object,
99 Undefined
100 };
101
103 JsonValue();
105 JsonValue(bool b);
107 JsonValue(int i);
109 JsonValue(int64_t i);
111 JsonValue(uint64_t i);
113 JsonValue(double d);
115 JsonValue(const char *s);
117 JsonValue(const promeki::String &s);
119 inline JsonValue(const JsonObject &obj);
121 inline JsonValue(const JsonArray &arr);
122
124 static JsonValue undefined();
125
127 static JsonValue fromVariant(const Variant &val);
128
130 Type type() const;
131
133 bool isNull() const { return _d->j.is_null(); }
135 bool isBool() const { return _d->j.is_boolean(); }
137 bool isDouble() const { return _d->j.is_number(); }
139 bool isString() const { return _d->j.is_string(); }
141 bool isArray() const { return _d->j.is_array(); }
143 bool isObject() const { return _d->j.is_object(); }
152 bool isUndefined() const { return _d->j.is_discarded(); }
153
155 bool toBool(bool def = false) const;
157 int64_t toInt(int64_t def = 0) const;
159 uint64_t toUInt(uint64_t def = 0) const;
161 double toDouble(double def = 0.0) const;
163 promeki::String toString(const promeki::String &def = promeki::String()) const;
165 inline JsonObject toObject() const;
167 inline JsonArray toArray() const;
169 Variant toVariant() const;
170
172 promeki::String toJsonString(unsigned int indent = 0) const;
173
175 bool operator==(const JsonValue &other) const;
177 bool operator!=(const JsonValue &other) const { return !(*this == other); }
178
179 private:
180 friend class JsonObject;
181 friend class JsonArray;
182
183 SharedPtr<JsonData> _d = SharedPtr<JsonData>::create();
184
185 explicit JsonValue(nlohmann::json j) : _d(SharedPtr<JsonData>::create(std::move(j))) {}
186 explicit JsonValue(const SharedPtr<JsonData> &d) : _d(d) {}
187
188 template <typename T> static T extract(const nlohmann::json &val, bool *good);
189};
190
240class JsonObject {
241 public:
248 static JsonObject parse(const String &str, Error *err = nullptr) {
249 try {
250 nlohmann::json j = nlohmann::json::parse(str.str());
251 if (!j.is_object()) throw std::runtime_error("not an object");
252 if (err) *err = Error::Ok;
253 return JsonObject(std::move(j));
254 } catch (...) {
255 if (err) *err = Error::Invalid;
256 return JsonObject();
257 }
258 }
259
268 static Result<JsonObject> fromString(const String &str) {
269 Error e;
270 JsonObject o = parse(str, &e);
271 if (e.isError()) return makeError<JsonObject>(e);
272 return makeResult(std::move(o));
273 }
274
276 static JsonObject fromVariantMap(const VariantMap &map);
277
279 JsonObject() = default;
280
282 int size() const { return static_cast<int>(_d->j.size()); }
283
285 int count() const { return size(); }
286
288 bool isEmpty() const { return _d->j.empty(); }
289
291 bool isValid() const { return !isEmpty(); }
292
294 List<String> keys() const;
295
301 bool valueIsNull(const String &key) const {
302 auto it = _d->j.find(key.str());
303 return it != _d->j.end() && it->is_null();
304 }
305
311 bool valueIsObject(const String &key) const {
312 auto it = _d->j.find(key.str());
313 return it != _d->j.end() && it->is_object();
314 }
315
321 bool valueIsArray(const String &key) const {
322 auto it = _d->j.find(key.str());
323 return it != _d->j.end() && it->is_array();
324 }
325
331 bool contains(const String &key) const { return _d->j.contains(key.str()); }
332
341 JsonValue value(const String &key) const;
342
344 JsonValue operator[](const String &key) const { return value(key); }
345
352 bool getBool(const String &key, Error *err = nullptr) const { return get<bool>(key, err); }
353
360 int64_t getInt(const String &key, Error *err = nullptr) const { return get<int64_t>(key, err); }
361
368 uint64_t getUInt(const String &key, Error *err = nullptr) const { return get<uint64_t>(key, err); }
369
376 double getDouble(const String &key, Error *err = nullptr) const { return get<double>(key, err); }
377
384 String getString(const String &key, Error *err = nullptr) const {
385 return get<std::string>(key, err);
386 }
387
394 JsonObject getObject(const String &key, Error *err = nullptr) const {
395 auto it = _d->j.find(key.str());
396 if (it == _d->j.end() || !it->is_object()) {
397 if (err) *err = Error::Invalid;
398 return JsonObject();
399 }
400 if (err) *err = Error::Ok;
401 return JsonObject(*it);
402 }
403
410 inline JsonArray getArray(const String &key, Error *err = nullptr) const;
411
417 String toString(unsigned int indent = 0) const {
418 if (indent == 0) return _d->j.dump();
419 return _d->j.dump(static_cast<int>(indent));
420 }
421
423 VariantMap toVariantMap() const;
424
426 void clear() { _d.modify()->j.clear(); }
427
432 bool remove(const String &key);
433
438 JsonValue take(const String &key);
439
444 void setNull(const String &key) { _d.modify()->j[key.str()] = nullptr; }
445
451 inline void set(const String &key, const JsonObject &val);
452
454 inline void set(const String &key, JsonObject &&val);
455
461 inline void set(const String &key, const JsonArray &val);
462
464 inline void set(const String &key, JsonArray &&val);
465
467 void set(const String &key, const JsonValue &val);
468
470 void set(const String &key, bool val) { _d.modify()->j[key.str()] = val; }
472 void set(const String &key, int val) { _d.modify()->j[key.str()] = val; }
474 void set(const String &key, unsigned int val) { _d.modify()->j[key.str()] = val; }
476 void set(const String &key, int64_t val) { _d.modify()->j[key.str()] = val; }
478 void set(const String &key, uint64_t val) { _d.modify()->j[key.str()] = val; }
480 void set(const String &key, float val) { _d.modify()->j[key.str()] = val; }
482 void set(const String &key, double val) { _d.modify()->j[key.str()] = val; }
484 void set(const String &key, const char *val) {
485 _d.modify()->j[key.str()] = std::string(val);
486 }
488 void set(const String &key, const String &val) {
489 _d.modify()->j[key.str()] = val.str();
490 }
492 void set(const String &key, const UUID &val);
493
499 void setFromVariant(const String &key, const Variant &val);
500
505 void forEach(Function<void(const String &key, const Variant &val)> func) const;
506
508 bool operator==(const JsonObject &other) const {
509 if (_d == other._d) return true;
510 return _d->j == other._d->j;
511 }
512
514 bool operator!=(const JsonObject &other) const { return !(*this == other); }
515
516 private:
517 friend class JsonArray;
518 friend class JsonValue;
519
520 SharedPtr<JsonData> _d = SharedPtr<JsonData>::create(JsonData(nlohmann::json::object()));
521
523 explicit JsonObject(nlohmann::json j) : _d(SharedPtr<JsonData>::create(JsonData(std::move(j)))) {}
524
526 explicit JsonObject(const SharedPtr<JsonData> &d) : _d(d) {}
527
528 template <typename T> T get(const String &key, Error *err = nullptr) const {
529 auto it = _d->j.find(key.str());
530 if (it == _d->j.end()) {
531 if (err) *err = Error::Invalid;
532 return T{};
533 }
534 bool good = false;
535 T ret = JsonValue::extract<T>(*it, &good);
536 if (err) *err = good ? Error::Ok : Error::Invalid;
537 return ret;
538 }
539};
540
561class JsonArray {
562 public:
569 static JsonArray parse(const String &str, Error *err = nullptr) {
570 try {
571 nlohmann::json j = nlohmann::json::parse(str.str());
572 if (!j.is_array()) throw std::runtime_error("not an array");
573 if (err) *err = Error::Ok;
574 return JsonArray(std::move(j));
575 } catch (...) {
576 if (err) *err = Error::Invalid;
577 return JsonArray();
578 }
579 }
580
588 static Result<JsonArray> fromString(const String &str) {
589 Error e;
590 JsonArray a = parse(str, &e);
591 if (e.isError()) return makeError<JsonArray>(e);
592 return makeResult(std::move(a));
593 }
594
596 static JsonArray fromVariantList(const VariantList &list);
597
599 JsonArray() = default;
600
602 int size() const { return static_cast<int>(_d->j.size()); }
603
605 int count() const { return size(); }
606
608 bool isEmpty() const { return _d->j.empty(); }
609
611 bool isValid() const { return !isEmpty(); }
612
617 bool valueIsNull(int index) const {
618 return index >= 0 && index < size() && _d->j[index].is_null();
619 }
620
625 bool valueIsObject(int index) const {
626 return index >= 0 && index < size() && _d->j[index].is_object();
627 }
628
633 bool valueIsArray(int index) const {
634 return index >= 0 && index < size() && _d->j[index].is_array();
635 }
636
643 JsonValue at(int index) const;
644
646 JsonValue operator[](int index) const { return at(index); }
647
649 JsonValue first() const { return at(0); }
650
652 JsonValue last() const { return at(size() - 1); }
653
660 bool getBool(int index, Error *err = nullptr) const { return get<bool>(index, err); }
661
668 int64_t getInt(int index, Error *err = nullptr) const { return get<int64_t>(index, err); }
669
676 uint64_t getUInt(int index, Error *err = nullptr) const { return get<uint64_t>(index, err); }
677
684 double getDouble(int index, Error *err = nullptr) const { return get<double>(index, err); }
685
692 String getString(int index, Error *err = nullptr) const { return get<std::string>(index, err); }
693
700 JsonObject getObject(int index, Error *err = nullptr) const {
701 if (index < 0 || index >= size() || !_d->j[index].is_object()) {
702 if (err) *err = Error::Invalid;
703 return JsonObject();
704 }
705 if (err) *err = Error::Ok;
706 return JsonObject(_d->j[index]);
707 }
708
715 JsonArray getArray(int index, Error *err = nullptr) const {
716 if (index < 0 || index >= size() || !_d->j[index].is_array()) {
717 if (err) *err = Error::Invalid;
718 return JsonArray();
719 }
720 if (err) *err = Error::Ok;
721 return JsonArray(_d->j[index]);
722 }
723
729 String toString(unsigned int indent = 0) const {
730 if (indent == 0) return _d->j.dump();
731 return _d->j.dump(static_cast<int>(indent));
732 }
733
735 VariantList toVariantList() const;
736
738 void clear() { _d.modify()->j.clear(); }
739
744 bool removeAt(int index);
745
750 JsonValue takeAt(int index);
751
753 void addNull() { _d.modify()->j.push_back(nullptr); }
755 inline void add(const JsonObject &val);
757 inline void add(JsonObject &&val);
759 inline void add(const JsonArray &val);
761 inline void add(JsonArray &&val);
763 void add(const JsonValue &val);
765 void add(bool val) { _d.modify()->j.push_back(val); }
767 void add(int val) { _d.modify()->j.push_back(val); }
769 void add(unsigned int val) { _d.modify()->j.push_back(val); }
771 void add(int64_t val) { _d.modify()->j.push_back(val); }
773 void add(uint64_t val) { _d.modify()->j.push_back(val); }
775 void add(float val) { _d.modify()->j.push_back(val); }
777 void add(double val) { _d.modify()->j.push_back(val); }
779 void add(const char *val) { _d.modify()->j.push_back(std::string(val)); }
781 void add(const String &val) { _d.modify()->j.push_back(val.str()); }
783 void add(const UUID &val);
784
789 void addFromVariant(const Variant &val);
790
792 template <typename T> void append(T &&val) { add(std::forward<T>(val)); }
793
801 void insert(int index, const JsonValue &val);
802
804 void prepend(const JsonValue &val) { insert(0, val); }
805
810 void forEach(Function<void(const Variant &val)> func) const;
811
813 bool operator==(const JsonArray &other) const {
814 if (_d == other._d) return true;
815 return _d->j == other._d->j;
816 }
817
819 bool operator!=(const JsonArray &other) const { return !(*this == other); }
820
822 class const_iterator {
823 public:
824 using iterator_category = std::forward_iterator_tag;
825 using value_type = JsonValue;
826 using difference_type = std::ptrdiff_t;
827 using pointer = void;
828 using reference = JsonValue;
829
830 const_iterator() = default;
831
832 JsonValue operator*() const;
833 const_iterator &operator++() {
834 ++_index;
835 return *this;
836 }
837 const_iterator operator++(int) {
838 const_iterator tmp = *this;
839 ++_index;
840 return tmp;
841 }
842 bool operator==(const const_iterator &other) const {
843 return _arr == other._arr && _index == other._index;
844 }
845 bool operator!=(const const_iterator &other) const { return !(*this == other); }
846
847 private:
848 friend class JsonArray;
849 const_iterator(const JsonArray *arr, int index) : _arr(arr), _index(index) {}
850
851 const JsonArray *_arr = nullptr;
852 int _index = 0;
853 };
854
856 const_iterator begin() const { return const_iterator(this, 0); }
858 const_iterator end() const { return const_iterator(this, size()); }
860 const_iterator cbegin() const { return begin(); }
862 const_iterator cend() const { return end(); }
863
864 private:
865 friend class JsonObject;
866 friend class JsonValue;
867
868 SharedPtr<JsonData> _d = SharedPtr<JsonData>::create(JsonData(nlohmann::json::array()));
869
871 explicit JsonArray(nlohmann::json j) : _d(SharedPtr<JsonData>::create(JsonData(std::move(j)))) {}
872
874 explicit JsonArray(const SharedPtr<JsonData> &d) : _d(d) {}
875
876 template <typename T> T get(int index, Error *err = nullptr) const {
877 if (index < 0 || index >= size()) {
878 if (err) *err = Error::Invalid;
879 return T{};
880 }
881 bool good = false;
882 T ret = JsonValue::extract<T>(_d->j[index], &good);
883 if (err) *err = good ? Error::Ok : Error::Invalid;
884 return ret;
885 }
886};
887
888// ============================================================================
889// Inline definitions that depend on multiple types being complete
890// ============================================================================
891
892inline JsonValue::JsonValue(const JsonObject &obj) : _d(obj._d) {}
893inline JsonValue::JsonValue(const JsonArray &arr) : _d(arr._d) {}
894
895template <typename T> T JsonValue::extract(const nlohmann::json &val, bool *good) {
896 T ret{};
897 bool ok = false;
898 try {
899 if constexpr (std::is_same_v<T, bool>) {
900 if (val.is_boolean()) {
901 ret = val.get<bool>();
902 ok = true;
903 } else if (val.is_number_integer()) {
904 ret = val.get<int64_t>() != 0;
905 ok = true;
906 }
907 } else if constexpr (std::is_integral_v<T>) {
908 if (val.is_number()) {
909 ret = static_cast<T>(val.get<int64_t>());
910 ok = true;
911 } else if (val.is_boolean()) {
912 ret = val.get<bool>() ? 1 : 0;
913 ok = true;
914 }
915 } else if constexpr (std::is_floating_point_v<T>) {
916 if (val.is_number()) {
917 ret = val.get<T>();
918 ok = true;
919 }
920 } else if constexpr (std::is_same_v<T, std::string>) {
921 if (val.is_string()) {
922 ret = val.get<std::string>();
923 ok = true;
924 } else if (!val.is_null() && !val.is_discarded()) {
925 ret = val.dump();
926 ok = true;
927 }
928 }
929 } catch (...) {}
930 if (good) *good = ok;
931 return ret;
932}
933
934inline JsonObject JsonValue::toObject() const {
935 if (!_d->j.is_object()) return JsonObject();
936 return JsonObject(_d);
937}
938
939inline JsonArray JsonValue::toArray() const {
940 if (!_d->j.is_array()) return JsonArray();
941 return JsonArray(_d);
942}
943
944inline JsonArray JsonObject::getArray(const String &key, Error *err) const {
945 auto it = _d->j.find(key.str());
946 if (it == _d->j.end() || !it->is_array()) {
947 if (err) *err = Error::Invalid;
948 return JsonArray();
949 }
950 if (err) *err = Error::Ok;
951 return JsonArray(*it);
952}
953
954inline void JsonObject::set(const String &key, const JsonObject &val) {
955 _d.modify()->j[key.str()] = val._d->j;
956}
957
958inline void JsonObject::set(const String &key, JsonObject &&val) {
959 // If the source is uniquely owned, modify() returns the data
960 // without detaching and we can safely move-out the inner json.
961 // Otherwise modify() does a deep-copy detach, then we move from
962 // that fresh copy — same total cost as the lvalue overload.
963 _d.modify()->j[key.str()] = std::move(val._d.modify()->j);
964}
965
966inline void JsonObject::set(const String &key, const JsonArray &val) {
967 _d.modify()->j[key.str()] = val._d->j;
968}
969
970inline void JsonObject::set(const String &key, JsonArray &&val) {
971 _d.modify()->j[key.str()] = std::move(val._d.modify()->j);
972}
973
974inline void JsonArray::add(const JsonObject &val) { _d.modify()->j.push_back(val._d->j); }
975
976inline void JsonArray::add(JsonObject &&val) { _d.modify()->j.push_back(std::move(val._d.modify()->j)); }
977
978inline void JsonArray::add(const JsonArray &val) { _d.modify()->j.push_back(val._d->j); }
979
980inline void JsonArray::add(JsonArray &&val) { _d.modify()->j.push_back(std::move(val._d.modify()->j)); }
981
982inline JsonValue JsonArray::const_iterator::operator*() const { return _arr->at(_index); }
983
984// The DataStream <<, >> operators for JsonObject / JsonArray live in
985// datastream.h's bottom half (after json.h is pulled in) so this
986// header can stay free of any datastream.h dependency — that's what
987// lets datastream.h's @ref Detail::makeDefaultOps populate the
988// @c toJson / @c fromJson Ops slots without a circular include.
989
990PROMEKI_NAMESPACE_END
991
992PROMEKI_FORMAT_VIA_TOSTRING(promeki::JsonObject);
993PROMEKI_FORMAT_VIA_TOSTRING(promeki::JsonArray);
994
995#endif // PROMEKI_ENABLE_CORE