File simplejson.h
File List > code_source > templat > include > TempLat > util > json > simplejson.h
Go to the documentation of this file
#ifndef NBSDX_SIMPLEJSON_H
#define NBSDX_SIMPLEJSON_H
/* not made by Wessel Valkenburg, not tested, not licensed. But works perfectly. */
// https://github.com/nbsdx/SimpleJSON
/* License
Do what the fuck you want public license
*/
#pragma once
#include <cstdint>
#include <cctype>
#include <string>
#include <deque>
#include <map>
#include <type_traits>
#include <initializer_list>
#include <ostream>
#include <iostream>
#include <sstream>
#include <numeric>
#include <cmath>
#include "TempLat/util/stringtrimmer.h"
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;
template <typename T>
requires std::is_floating_point_v<T>
auto to_string(const T &value)
{
char buffer[64];
char fmt[64];
snprintf(fmt, 64, "%%%i.%ie", std::numeric_limits<T>::max_digits10 + 9, std::numeric_limits<T>::max_digits10);
snprintf(buffer, 64, fmt, value);
return std::string(buffer);
}
namespace
{
inline 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 output;
}
} // namespace
class JSON
{
union BackingData {
BackingData(double d) : Float(d) {}
BackingData(long 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;
long Int;
bool Bool;
} Internal;
public:
enum class Class { Null, Object, Array, String, Floating, Integral, Boolean };
/* wv: class to string */
static const char *ClassString(Class c)
{
switch (c) {
case Class::Object:
return "Object";
case Class::Array:
return "Array";
case Class::String:
return "String";
case Class::Floating:
return "Floating";
case Class::Integral:
return "Integral";
case Class::Boolean:
return "Boolean";
case Class::Null:
default:
return "null";
}
}
template <typename Container> class JSONWrapper
{
Container *object;
public:
JSONWrapper(Container *val) : object(val) {}
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:
JSONConstWrapper(const Container *val) : object(val) {}
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) {}
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)
{
/* WV fixing memory leak */
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)
{
/* WV new: here is a potential memory leak; need to delete whatever we had. This is for example implemented in
* settype, so... */
/* WV fixing memory leak */
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>
JSON(T b, typename enable_if<is_same<T, bool>::value>::type * = 0) : Internal(b), Type(Class::Boolean)
{
}
template <typename T>
JSON(T i, typename enable_if<is_integral<T>::value && !is_same<T, bool>::value>::type * = 0)
: Internal((long)i), Type(Class::Integral)
{
}
template <typename T>
JSON(T f, typename enable_if<is_floating_point<T>::value>::type * = 0) : Internal((double)f), Type(Class::Floating)
{
}
template <typename T>
JSON(T s, typename enable_if<is_convertible<T, string>::value>::type * = 0)
: Internal(string(s)), Type(Class::String)
{
}
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;
}
/* WV New: size disregarding null elements. Slow! Beware! */
int size_nonnull() const
{
if (Type == Class::Object) {
int count = 0;
for (auto &&it : ObjectRange()) {
if (it.second.JSONType() != Class::Null) ++count;
}
return count;
} else if (Type == Class::Array) {
int count = 0;
for (auto &&it : ArrayRange()) {
if (it.JSONType() != Class::Null) ++count;
}
return count;
} else
return -1;
}
Class JSONType() const { return Type; }
bool IsNull() const { return Type == Class::Null; }
string ToString() const
{
bool b;
return ToString(b);
}
string ToString(bool &ok) const
{
ok = (Type == Class::String);
return ok ? 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;
}
long ToInt() const
{
bool b;
return ToInt(b);
}
long 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;
}
string ValueAsString() const
{
std::stringstream str;
if (Type == Class::String)
return ToString();
else if (Type == Class::Floating) {
char buffer[32];
snprintf(buffer, 32, "%25.16g", Internal.Float);
/* remove superfluous zeros? */
str << buffer;
} else {
str << *this;
}
return TempLat::StringTrimmer::trimmed(str.str());
}
double ValueAsFloating() const
{
if (Type == Class::Floating)
return Internal.Float;
else if (Type == Class::Integral)
return Internal.Int;
else if (Type == Class::Boolean)
return Internal.Bool;
return 0.;
}
long long int ValueAsIntegral() const
{
if (Type == Class::Floating)
return Internal.Float;
else if (Type == Class::Integral)
return Internal.Int;
else if (Type == Class::Boolean)
return Internal.Bool;
return 0;
}
bool ValueAsBoolean() const
{
if (Type == Class::Floating)
return Internal.Float;
else if (Type == Class::Integral)
return Internal.Int;
else if (Type == Class::Boolean)
return Internal.Bool;
return false;
}
/* for unified syntax: Value<double>() */
template <typename T> typename std::enable_if<std::is_floating_point<T>::value, T>::type Value()
{
return ValueAsFloating();
}
template <typename T> typename std::enable_if<std::is_integral<T>::value, T>::type Value()
{
return ValueAsIntegral();
}
template <typename T> typename std::enable_if<std::is_same<T, bool>::value, T>::type Value()
{
return ValueAsBoolean();
}
template <typename T> typename std::enable_if<std::is_same<T, std::string>::value, T>::type Value()
{
return ValueAsString();
}
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: /* WV: added quotation marks around NaN / Inf, because JSON standard does not want to parse
those otherwise. */
if (std::isfinite(Internal.Float))
return json::to_string(Internal.Float);
else
return "\"" + std::to_string(Internal.Float) + "\"";
case Class::Integral: /* WV: added quotation marks around NaN / Inf, because JSON standard does not want to parse
those otherwise. */
if (std::isfinite(Internal.Int))
return std::to_string(Internal.Int);
else
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 &);
/* WV New: erase by value from array */
template <typename T> void EraseByValue(T val)
{
if (Type == Class::Array) {
auto range = ArrayRange();
Internal.List->erase(std::remove(range.begin(), range.end(), val), range.end());
}
}
private:
void SetType(Class type)
{
if (type == Type) return;
// switch( Type ) {
// case Class::Object: delete Internal.Map; break;
// case Class::Array: delete Internal.List; break;
// case Class::String: delete Internal.String; break;
// default:;
// }
/* WV New: moved to function */
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;
}
/* WV New: need to separate this out, because it should be called more often!! */
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;
};
inline JSON Array() { return JSON::Make(JSON::Class::Array); }
template <typename... T> JSON Array(T... args)
{
JSON arr = JSON::Make(JSON::Class::Array);
arr.append(args...);
return arr;
}
inline JSON Object() { return JSON::Make(JSON::Class::Object); }
inline std::ostream &operator<<(std::ostream &os, const JSON &json)
{
os << json.dump();
return os;
}
namespace
{
JSON parse_next(const string &, size_t &);
inline void consume_ws(const string &str, size_t &offset)
{
while (isspace(str[offset]))
++offset;
}
inline JSON parse_object(const string &str, size_t &offset)
{
JSON Object = JSON::Make(JSON::Class::Object);
++offset;
consume_ws(str, offset);
if (str[offset] == '}') {
++offset;
return 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 << "From input string \"" << str << "\" with length " << str.length() << " at offset:
// " << offset << "\n";
std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n";
break;
}
}
return Object;
}
inline JSON parse_array(const string &str, size_t &offset)
{
JSON Array = JSON::Make(JSON::Class::Array);
unsigned index = 0;
++offset;
consume_ws(str, offset);
if (str[offset] == ']') {
++offset;
return 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 JSON::Make(JSON::Class::Array);
}
}
return Array;
}
inline JSON parse_string(const string &str, size_t &offset)
{
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 JSON::Make(JSON::Class::String);
}
}
offset += 4;
} break;
default:
val += '\\';
break;
}
} else
val += c;
}
++offset;
String = val;
return String;
}
inline JSON parse_number(const string &str, size_t &offset)
{
/* wv: have been fixing this routine.
Rule is: offset points to character to be read and treated, c is character of previous offset. c = str[offset++];
*/
JSON Number;
string val, exp_str;
char c;
bool isDouble = false;
long exp = 0;
while (true) {
c = str[offset++];
if ((c == '-') || (c >= '0' && c <= '9'))
val += c;
else if (c == '.') {
val += c;
isDouble = true;
} else {
/* wv: character is untreated, but later offset will increment anyway by c = str[offset++], so... */
--offset;
break;
}
}
if (c == 'E' || c == 'e') {
/* last read character has now been treated again, hence: */
++offset;
c = str[offset]; /* no increment! Only if c == '-' */
if (c == '-') {
++offset;
exp_str += '-';
}
if (c == '+') {
++offset;
exp_str += '+'; /* can be left out */
}
while (true) {
c = str[offset++];
if (c >= '0' && c <= '9')
exp_str += c;
else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
std::cerr << str << " - " << exp_str << "\n";
std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n";
return JSON::Make(JSON::Class::Null);
} else {
/* wv: character is untreated, but later offset will increment anyway, so... */
--offset;
break;
}
}
exp = std::stol(exp_str);
} else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
std::cerr << "ERROR: Number: unexpected character '" << c << "'\n";
return JSON::Make(JSON::Class::Null);
}
// this --offset was erroneous; see 'untreated character' comment above. */
// --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 Number;
}
inline JSON parse_bool(const string &str, size_t &offset)
{
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 JSON::Make(JSON::Class::Null);
}
offset += (Bool.ToBool() ? 4 : 5);
return Bool;
}
inline JSON parse_null(const string &str, size_t &offset)
{
JSON Null;
if (str.substr(offset, 4) != "null") {
std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr(offset, 4) << "'\n";
return JSON::Make(JSON::Class::Null);
}
offset += 4;
return Null;
}
inline JSON parse_next(const string &str, size_t &offset)
{
char value;
consume_ws(str, offset);
value = str[offset];
switch (value) {
case '[':
return parse_array(str, offset);
case '{':
return parse_object(str, offset);
case '\"':
return parse_string(str, offset);
case 't':
case 'f':
return parse_bool(str, offset);
case 'n':
return parse_null(str, offset);
default:
if ((value <= '9' && value >= '0') || value == '-') return parse_number(str, offset);
}
std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n";
return JSON();
}
} // namespace
inline JSON JSON::Load(const string &str)
{
size_t offset = 0;
return parse_next(str, offset);
}
/* WV new: added automatic array to array using range */
template <typename T> JSON ArrayFromRangeObject(T arr)
{
auto newarr = Array();
for (auto &&it : arr) {
newarr.append(it);
}
return newarr;
}
inline bool operator==(const JSON a, const JSON b)
{
/* ok, deep traversal, here we go... */
if (a.JSONType() != b.JSONType() && a.JSONType() != JSON::Class::Array && a.JSONType() != JSON::Class::Object &&
a.JSONType() != JSON::Class::Null) {
if (a.JSONType() == JSON::Class::String || b.JSONType() == JSON::Class::String)
return a.ValueAsString() == b.ValueAsString();
else if (a.JSONType() == JSON::Class::Floating)
return a.ValueAsFloating() == b.ValueAsFloating();
else if (a.JSONType() == JSON::Class::Integral) {
if (b.JSONType() == JSON::Class::Floating) return a.ValueAsFloating() == b.ValueAsFloating();
return a.ValueAsIntegral() == b.ValueAsIntegral();
} else if (a.JSONType() == JSON::Class::Boolean) {
if (b.JSONType() == JSON::Class::Floating)
return a.ValueAsFloating() == b.ValueAsFloating();
else if (b.JSONType() == JSON::Class::Integral)
return a.ValueAsIntegral() == b.ValueAsIntegral();
return a.ValueAsBoolean() == b.ValueAsBoolean();
}
return false;
}
if (a.JSONType() == JSON::Class::Array) {
/* dangerous: different order means they are unequal! */
if (a.size_nonnull() != b.size_nonnull()) return false;
auto aRange = a.ArrayRange();
auto bRange = b.ArrayRange();
auto ita = aRange.begin();
auto itb = bRange.begin();
for (; ita < aRange.end() && itb < bRange.end(); ++ita, ++itb) {
if (!(*ita == *itb) && (ita->JSONType() != JSON::Class::Null || itb->JSONType() != JSON::Class::Null)) {
return false;
}
/* this is where this routine becomes recursive, as they are json objects */
/* we ignore NULL objects!! They are consequence of testing a object for an absent entry. Should not be
* punished. */
}
} else if (a.JSONType() == JSON::Class::Object) {
if (a.size_nonnull() != b.size_nonnull()) return false;
auto aRange = a.ObjectRange();
for (auto ita = aRange.begin(); ita != aRange.end(); ++ita) {
if (!b.hasKey(ita->first) && ita->second.JSONType() != JSON::Class::Null) {
return false;
}
if (b.hasKey(ita->first) && !(ita->second == b.at(ita->first)) &&
(ita->second.JSONType() != JSON::Class::Null || b.at(ita->first).JSONType() != JSON::Class::Null)) {
return false;
}
}
} else { /* if it is a value, tostring should work for all types */
return a.ValueAsString() == b.ValueAsString();
}
/* if we're here, they're equal */
return true;
}
inline bool operator!=(const JSON a, const JSON b) { return !(a == b); }
inline bool operator>(const JSON a, const JSON b)
{
/* ok, deep traversal, here we go... */
if (a.JSONType() == JSON::Class::Array) {
/* for arrays, we do as with strings: return the first inequality in the two lists that we walk in parallel */
/* dangerous: different order means they are unequal! */
if (a.size_nonnull() != b.size_nonnull()) return false;
auto aRange = a.ArrayRange();
auto bRange = b.ArrayRange();
auto ita = aRange.begin();
auto itb = bRange.begin();
for (; ita < aRange.end() && itb < bRange.end(); ++ita, ++itb) {
if (!(*ita == *itb))
return *ita > *itb; /* this is where this routine becomes recursive, as they are json objects */
}
} else if (a.JSONType() == JSON::Class::Object) {
auto aRange = a.ObjectRange();
for (auto ita = aRange.begin(); ita != aRange.end(); ++ita) {
if (!b.hasKey(ita->first)) return ita->second > 0; /* because we treat absence as zero */
if (!(ita->second == b.at(ita->first))) return ita->second > b.at(ita->first);
}
} else { /* if it is a value, tostring should work for all types */
/* switch over all the types
String,
Floating,
Integral,
Boolean */
if (a.JSONType() == JSON::Class::String || b.JSONType() == JSON::Class::String)
return a.ValueAsString() > b.ValueAsString();
else if (a.JSONType() == JSON::Class::Floating)
return a.ValueAsFloating() > b.ValueAsFloating();
else if (a.JSONType() == JSON::Class::Integral) {
if (b.JSONType() == JSON::Class::Floating) return a.ValueAsFloating() > b.ValueAsFloating();
return a.ValueAsIntegral() > b.ValueAsIntegral();
} else if (a.JSONType() == JSON::Class::Boolean) {
if (b.JSONType() == JSON::Class::Floating)
return a.ValueAsFloating() > b.ValueAsFloating();
else if (b.JSONType() == JSON::Class::Integral)
return a.ValueAsIntegral() > b.ValueAsIntegral();
return a.ValueAsBoolean() > b.ValueAsBoolean();
}
/* fallback */
return a.ValueAsFloating() > b.ValueAsFloating();
}
/* fallback */
return a.ValueAsFloating() > b.ValueAsFloating();
}
inline bool operator>=(const JSON a, const JSON b) { return (a > b || a == b); }
inline bool operator<(const JSON a, const JSON b) { return !(a >= b); }
inline bool operator<=(const JSON a, const JSON b) { return !(a > b); }
/* WV operator % */
inline long long int operator%(const JSON a, long long int b) { return a.ValueAsIntegral() % b; }
} // End Namespace json
#endif