You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

754 lines
19 KiB

// Copyright (c) From https://github.com/nbsdx/SimpleJSON
// 2022 Binbin Zhang (binbzha@qq.com)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef UTILS_JSON_H_
#define UTILS_JSON_H_
#include <cctype>
#include <cmath>
#include <cstdint>
#include <deque>
#include <initializer_list>
#include <iostream>
#include <map>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
namespace json {
using std::deque;
using std::enable_if;
using std::initializer_list;
using std::is_convertible;
using std::is_floating_point;
using std::is_integral;
using std::is_same;
using std::map;
using std::string;
namespace { // NOLINT
string json_escape(const string& str) {
string output;
for (unsigned i = 0; i < str.length(); ++i) switch (str[i]) {
case '\"':
output += "\\\"";
break;
case '\\':
output += "\\\\";
break;
case '\b':
output += "\\b";
break;
case '\f':
output += "\\f";
break;
case '\n':
output += "\\n";
break;
case '\r':
output += "\\r";
break;
case '\t':
output += "\\t";
break;
default:
output += str[i];
break;
}
return std::move(output);
}
} // namespace
class JSON {
union BackingData {
BackingData(double d) : Float(d) {}
BackingData(int l) : Int(l) {}
BackingData(bool b) : Bool(b) {}
BackingData(string s) : String(new string(s)) {}
BackingData() : Int(0) {}
deque<JSON>* List;
map<string, JSON>* Map;
string* String;
double Float;
int Int;
bool Bool;
} Internal;
public:
enum class Class { Null, Object, Array, String, Floating, Integral, Boolean };
template <typename Container>
class JSONWrapper {
Container* object;
public:
explicit JSONWrapper(Container* val) : object(val) {}
explicit JSONWrapper(std::nullptr_t) : object(nullptr) {}
typename Container::iterator begin() {
return object ? object->begin() : typename Container::iterator();
}
typename Container::iterator end() {
return object ? object->end() : typename Container::iterator();
}
typename Container::const_iterator begin() const {
return object ? object->begin() : typename Container::iterator();
}
typename Container::const_iterator end() const {
return object ? object->end() : typename Container::iterator();
}
};
template <typename Container>
class JSONConstWrapper {
const Container* object;
public:
explicit JSONConstWrapper(const Container* val) : object(val) {}
explicit JSONConstWrapper(std::nullptr_t) : object(nullptr) {}
typename Container::const_iterator begin() const {
return object ? object->begin() : typename Container::const_iterator();
}
typename Container::const_iterator end() const {
return object ? object->end() : typename Container::const_iterator();
}
};
JSON() : Internal(), Type(Class::Null) {}
explicit JSON(initializer_list<JSON> list) : JSON() {
SetType(Class::Object);
for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i)
operator[](i->ToString()) = *std::next(i);
}
JSON(JSON&& other) : Internal(other.Internal), Type(other.Type) {
other.Type = Class::Null;
other.Internal.Map = nullptr;
}
JSON& operator=(JSON&& other) {
ClearInternal();
Internal = other.Internal;
Type = other.Type;
other.Internal.Map = nullptr;
other.Type = Class::Null;
return *this;
}
JSON(const JSON& other) {
switch (other.Type) {
case Class::Object:
Internal.Map = new map<string, JSON>(other.Internal.Map->begin(),
other.Internal.Map->end());
break;
case Class::Array:
Internal.List = new deque<JSON>(other.Internal.List->begin(),
other.Internal.List->end());
break;
case Class::String:
Internal.String = new string(*other.Internal.String);
break;
default:
Internal = other.Internal;
}
Type = other.Type;
}
JSON& operator=(const JSON& other) {
ClearInternal();
switch (other.Type) {
case Class::Object:
Internal.Map = new map<string, JSON>(other.Internal.Map->begin(),
other.Internal.Map->end());
break;
case Class::Array:
Internal.List = new deque<JSON>(other.Internal.List->begin(),
other.Internal.List->end());
break;
case Class::String:
Internal.String = new string(*other.Internal.String);
break;
default:
Internal = other.Internal;
}
Type = other.Type;
return *this;
}
~JSON() {
switch (Type) {
case Class::Array:
delete Internal.List;
break;
case Class::Object:
delete Internal.Map;
break;
case Class::String:
delete Internal.String;
break;
default: {
};
}
}
template <typename T>
explicit JSON(T b, typename enable_if<is_same<T, bool>::value>::type* = 0)
: Internal(b), Type(Class::Boolean) {}
template <typename T>
explicit JSON(T i, typename enable_if<is_integral<T>::value &&
!is_same<T, bool>::value>::type* = 0)
: Internal(static_cast<int>(i)), Type(Class::Integral) {}
template <typename T>
explicit JSON(T f, typename enable_if<is_floating_point<T>::value>::type* = 0)
: Internal(static_cast<double>(f)), Type(Class::Floating) {}
template <typename T>
explicit JSON(T s,
typename enable_if<is_convertible<T, string>::value>::type* = 0)
: Internal(string(s)), Type(Class::String) {}
explicit JSON(std::nullptr_t) : Internal(), Type(Class::Null) {}
static JSON Make(Class type) {
JSON ret;
ret.SetType(type);
return ret;
}
static JSON Load(const string&);
template <typename T>
void append(T arg) {
SetType(Class::Array);
Internal.List->emplace_back(arg);
}
template <typename T, typename... U>
void append(T arg, U... args) {
append(arg);
append(args...);
}
template <typename T>
typename enable_if<is_same<T, bool>::value, JSON&>::type operator=(T b) {
SetType(Class::Boolean);
Internal.Bool = b;
return *this;
}
template <typename T>
typename enable_if<is_integral<T>::value && !is_same<T, bool>::value,
JSON&>::type
operator=(T i) {
SetType(Class::Integral);
Internal.Int = i;
return *this;
}
template <typename T>
typename enable_if<is_floating_point<T>::value, JSON&>::type operator=(T f) {
SetType(Class::Floating);
Internal.Float = f;
return *this;
}
template <typename T>
typename enable_if<is_convertible<T, string>::value, JSON&>::type operator=(
T s) {
SetType(Class::String);
*Internal.String = string(s);
return *this;
}
JSON& operator[](const string& key) {
SetType(Class::Object);
return Internal.Map->operator[](key);
}
JSON& operator[](unsigned index) {
SetType(Class::Array);
if (index >= Internal.List->size()) Internal.List->resize(index + 1);
return Internal.List->operator[](index);
}
JSON& at(const string& key) { return operator[](key); }
const JSON& at(const string& key) const { return Internal.Map->at(key); }
JSON& at(unsigned index) { return operator[](index); }
const JSON& at(unsigned index) const { return Internal.List->at(index); }
int length() const {
if (Type == Class::Array)
return Internal.List->size();
else
return -1;
}
bool hasKey(const string& key) const {
if (Type == Class::Object)
return Internal.Map->find(key) != Internal.Map->end();
return false;
}
int size() const {
if (Type == Class::Object)
return Internal.Map->size();
else if (Type == Class::Array)
return Internal.List->size();
else
return -1;
}
Class JSONType() const { return Type; }
/// Functions for getting primitives from the JSON object.
bool IsNull() const { return Type == Class::Null; }
string ToString() const {
bool b;
return std::move(ToString(&b));
}
string ToString(bool* ok) const {
*ok = (Type == Class::String);
return *ok ? std::move(json_escape(*Internal.String)) : string("");
}
double ToFloat() const {
bool b;
return ToFloat(&b);
}
double ToFloat(bool* ok) const {
*ok = (Type == Class::Floating);
return *ok ? Internal.Float : 0.0;
}
int ToInt() const {
bool b;
return ToInt(&b);
}
int ToInt(bool* ok) const {
*ok = (Type == Class::Integral);
return *ok ? Internal.Int : 0;
}
bool ToBool() const {
bool b;
return ToBool(&b);
}
bool ToBool(bool* ok) const {
*ok = (Type == Class::Boolean);
return *ok ? Internal.Bool : false;
}
JSONWrapper<map<string, JSON>> ObjectRange() {
if (Type == Class::Object)
return JSONWrapper<map<string, JSON>>(Internal.Map);
return JSONWrapper<map<string, JSON>>(nullptr);
}
JSONWrapper<deque<JSON>> ArrayRange() {
if (Type == Class::Array) return JSONWrapper<deque<JSON>>(Internal.List);
return JSONWrapper<deque<JSON>>(nullptr);
}
JSONConstWrapper<map<string, JSON>> ObjectRange() const {
if (Type == Class::Object)
return JSONConstWrapper<map<string, JSON>>(Internal.Map);
return JSONConstWrapper<map<string, JSON>>(nullptr);
}
JSONConstWrapper<deque<JSON>> ArrayRange() const {
if (Type == Class::Array)
return JSONConstWrapper<deque<JSON>>(Internal.List);
return JSONConstWrapper<deque<JSON>>(nullptr);
}
string dump(int depth = 1, string tab = " ") const {
string pad = "";
for (int i = 0; i < depth; ++i, pad += tab) {
}
switch (Type) {
case Class::Null:
return "null";
case Class::Object: {
string s = "{\n";
bool skip = true;
for (auto& p : *Internal.Map) {
if (!skip) s += ",\n";
s += (pad + "\"" + p.first + "\" : " + p.second.dump(depth + 1, tab));
skip = false;
}
s += ("\n" + pad.erase(0, 2) + "}");
return s;
}
case Class::Array: {
string s = "[";
bool skip = true;
for (auto& p : *Internal.List) {
if (!skip) s += ", ";
s += p.dump(depth + 1, tab);
skip = false;
}
s += "]";
return s;
}
case Class::String:
return "\"" + json_escape(*Internal.String) + "\"";
case Class::Floating:
return std::to_string(Internal.Float);
case Class::Integral:
return std::to_string(Internal.Int);
case Class::Boolean:
return Internal.Bool ? "true" : "false";
default:
return "";
}
return "";
}
friend std::ostream& operator<<(std::ostream&, const JSON&);
private:
void SetType(Class type) {
if (type == Type) return;
ClearInternal();
switch (type) {
case Class::Null:
Internal.Map = nullptr;
break;
case Class::Object:
Internal.Map = new map<string, JSON>();
break;
case Class::Array:
Internal.List = new deque<JSON>();
break;
case Class::String:
Internal.String = new string();
break;
case Class::Floating:
Internal.Float = 0.0;
break;
case Class::Integral:
Internal.Int = 0;
break;
case Class::Boolean:
Internal.Bool = false;
break;
}
Type = type;
}
private:
/* beware: only call if YOU know that Internal is allocated. No checks
performed here. This function should be called in a constructed JSON just
before you are going to overwrite Internal...
*/
void ClearInternal() {
switch (Type) {
case Class::Object:
delete Internal.Map;
break;
case Class::Array:
delete Internal.List;
break;
case Class::String:
delete Internal.String;
break;
default: {
};
}
}
private:
Class Type = Class::Null;
};
JSON Array() { return std::move(JSON::Make(JSON::Class::Array)); }
template <typename... T>
JSON Array(T... args) {
JSON arr = JSON::Make(JSON::Class::Array);
arr.append(args...);
return std::move(arr);
}
JSON Object() { return std::move(JSON::Make(JSON::Class::Object)); }
std::ostream& operator<<(std::ostream& os, const JSON& json) {
os << json.dump();
return os;
}
namespace { // NOLINT
JSON parse_next(const string&, size_t&);
void consume_ws(const string& str, size_t& offset) { // NOLINT
while (isspace(str[offset])) ++offset;
}
JSON parse_object(const string& str, size_t& offset) { // NOLINT
JSON Object = JSON::Make(JSON::Class::Object);
++offset;
consume_ws(str, offset);
if (str[offset] == '}') {
++offset;
return std::move(Object);
}
while (true) {
JSON Key = parse_next(str, offset);
consume_ws(str, offset);
if (str[offset] != ':') {
std::cerr << "Error: Object: Expected colon, found '" << str[offset]
<< "'\n";
break;
}
consume_ws(str, ++offset);
JSON Value = parse_next(str, offset);
Object[Key.ToString()] = Value;
consume_ws(str, offset);
if (str[offset] == ',') {
++offset;
continue;
} else if (str[offset] == '}') {
++offset;
break;
} else {
std::cerr << "ERROR: Object: Expected comma, found '" << str[offset]
<< "'\n";
break;
}
}
return std::move(Object);
}
JSON parse_array(const string& str, size_t& offset) { // NOLINT
JSON Array = JSON::Make(JSON::Class::Array);
unsigned index = 0;
++offset;
consume_ws(str, offset);
if (str[offset] == ']') {
++offset;
return std::move(Array);
}
while (true) {
Array[index++] = parse_next(str, offset);
consume_ws(str, offset);
if (str[offset] == ',') {
++offset;
continue;
} else if (str[offset] == ']') {
++offset;
break;
} else {
std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset]
<< "'\n";
return std::move(JSON::Make(JSON::Class::Array));
}
}
return std::move(Array);
}
JSON parse_string(const string& str, size_t& offset) { // NOLINT
JSON String;
string val;
for (char c = str[++offset]; c != '\"'; c = str[++offset]) {
if (c == '\\') {
switch (str[++offset]) {
case '\"':
val += '\"';
break;
case '\\':
val += '\\';
break;
case '/':
val += '/';
break;
case 'b':
val += '\b';
break;
case 'f':
val += '\f';
break;
case 'n':
val += '\n';
break;
case 'r':
val += '\r';
break;
case 't':
val += '\t';
break;
case 'u': {
val += "\\u";
for (unsigned i = 1; i <= 4; ++i) {
c = str[offset + i];
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F')) {
val += c;
} else {
std::cerr << "ERROR: String: Expected hex character in unicode "
"escape, found '"
<< c << "'\n";
return std::move(JSON::Make(JSON::Class::String));
}
}
offset += 4;
} break;
default:
val += '\\';
break;
}
} else {
val += c;
}
}
++offset;
String = val;
return std::move(String);
}
JSON parse_number(const string& str, size_t& offset) { // NOLINT
JSON Number;
string val, exp_str;
char c;
bool isDouble = false;
int exp = 0;
while (true) {
c = str[offset++];
if ((c == '-') || (c >= '0' && c <= '9')) {
val += c;
} else if (c == '.') {
val += c;
isDouble = true;
} else {
break;
}
}
if (c == 'E' || c == 'e') {
c = str[offset++];
if (c == '-') {
++offset;
exp_str += '-';
}
while (true) {
c = str[offset++];
if (c >= '0' && c <= '9') {
exp_str += c;
} else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
std::cerr << "ERROR: Number: Expected a number for exponent, found '"
<< c << "'\n";
return std::move(JSON::Make(JSON::Class::Null));
} else {
break;
}
}
exp = std::stol(exp_str);
} else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
std::cerr << "ERROR: Number: unexpected character '" << c << "'\n";
return std::move(JSON::Make(JSON::Class::Null));
}
--offset;
if (isDouble) {
Number = std::stod(val) * std::pow(10, exp);
} else {
if (!exp_str.empty())
Number = std::stol(val) * std::pow(10, exp);
else
Number = std::stol(val);
}
return std::move(Number);
}
JSON parse_bool(const string& str, size_t& offset) { // NOLINT
JSON Bool;
if (str.substr(offset, 4) == "true") {
Bool = true;
} else if (str.substr(offset, 5) == "false") {
Bool = false;
} else {
std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '"
<< str.substr(offset, 5) << "'\n";
return std::move(JSON::Make(JSON::Class::Null));
}
offset += (Bool.ToBool() ? 4 : 5);
return std::move(Bool);
}
JSON parse_null(const string& str, size_t& offset) { // NOLINT
JSON Null;
if (str.substr(offset, 4) != "null") {
std::cerr << "ERROR: Null: Expected 'null', found '"
<< str.substr(offset, 4) << "'\n";
return std::move(JSON::Make(JSON::Class::Null));
}
offset += 4;
return std::move(Null);
}
JSON parse_next(const string& str, size_t& offset) { // NOLINT
char value;
consume_ws(str, offset);
value = str[offset];
switch (value) {
case '[':
return std::move(parse_array(str, offset));
case '{':
return std::move(parse_object(str, offset));
case '\"':
return std::move(parse_string(str, offset));
case 't':
case 'f':
return std::move(parse_bool(str, offset));
case 'n':
return std::move(parse_null(str, offset));
default:
if ((value <= '9' && value >= '0') || value == '-')
return std::move(parse_number(str, offset));
}
std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n";
return JSON();
}
} // namespace
JSON JSON::Load(const string& str) {
size_t offset = 0;
return std::move(parse_next(str, offset));
}
} // namespace json
#endif // UTILS_JSON_H_