porymap/include/lib/orderedjson.h

261 lines
8.8 KiB
C
Raw Normal View History

2020-03-05 16:23:17 +00:00
/* poryjson
*
* poryjson is a modified version of json11, which adds support to preserve the key order
* when dumping json objects, and support to Qt classes
*
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
*
* The core object provided by the library is json11::Json. A Json object represents any JSON
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
* object (std::map).
*
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
* Json::parse (static) to parse a std::string as a Json object.
*
* Internally, the various types of Json object are represented by the JsonValue class
* hierarchy.
*
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
* so some JSON implementations distinguish between integers and floating-point numbers, while
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
* to JSON that will be *silently* changed by a round-trip through those implementations.
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
* provides integer helpers.
*
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
* will be exact for +/- 275 years.)
*/
/* Copyright (c) 2013 Dropbox, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
2020-09-13 23:37:55 +01:00
#pragma once
#ifndef ORDERED_JSON_H
#define ORDERED_JSON_H
2020-03-05 16:23:17 +00:00
#include <QString>
2020-03-05 16:40:07 +00:00
#include <QVector>
#include <QPair>
#include <QFile>
#include <QTextStream>
2020-03-05 16:40:07 +00:00
2020-03-05 16:23:17 +00:00
#include <memory>
#include <initializer_list>
#include <QtGlobal>
#include "orderedmap.h"
2020-03-05 16:23:17 +00:00
#ifdef _MSC_VER
2021-02-18 00:20:14 +00:00
#if _MSC_VER <= 1800 // VS 2013
#ifndef noexcept
#define noexcept throw()
#endif
#ifndef snprintf
#define snprintf _snprintf_s
#endif
#endif
2020-03-05 16:23:17 +00:00
#endif
namespace poryjson {
2021-02-18 00:20:14 +00:00
enum JsonParse { STANDARD, COMMENTS };
2020-03-05 16:23:17 +00:00
class JsonValue;
class Json final {
public:
// Types
2021-02-18 00:20:14 +00:00
enum Type { NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT };
2020-03-05 16:23:17 +00:00
// Array and object typedefs
2020-03-05 16:40:07 +00:00
typedef QVector<Json> array;
typedef tsl::ordered_map<QString, Json> object;
2020-03-05 16:23:17 +00:00
// Constructors for the various types of JSON value.
2021-02-18 00:20:14 +00:00
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const QString& value); // STRING
Json(QString&& value); // STRING
Json(const char* value); // STRING
Json(const array& values); // ARRAY
Json(array&& values); // ARRAY
Json(const object& values); // OBJECT
Json(object&& values); // OBJECT
2020-03-05 16:23:17 +00:00
// Implicit constructor: anything with a to_json() function.
2021-02-18 00:20:14 +00:00
template <class T, class = decltype(&T::to_json)> Json(const T& t) : Json(t.to_json()) {
}
2020-03-05 16:23:17 +00:00
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
2021-02-18 00:20:14 +00:00
template <class M, typename std::enable_if<std::is_constructible<QString, decltype(std::declval<M>().begin()->first)>::value
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
int>::type
= 0>
Json(const M& m)
: Json(object(m.begin(), m.end())) {
}
2020-03-05 16:23:17 +00:00
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
2021-02-18 00:20:14 +00:00
template <class V, typename std::enable_if<std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value, int>::type = 0>
Json(const V& v)
: Json(array(v.begin(), v.end())) {
}
2020-03-05 16:23:17 +00:00
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.
2021-02-18 00:20:14 +00:00
Json(void*) = delete;
2020-03-05 16:23:17 +00:00
// Accessors
Type type() const;
2021-02-18 00:20:14 +00:00
bool is_null() const {
return type() == NUL;
}
bool is_number() const {
return type() == NUMBER;
}
bool is_bool() const {
return type() == BOOL;
}
bool is_string() const {
return type() == STRING;
}
bool is_array() const {
return type() == ARRAY;
}
bool is_object() const {
return type() == OBJECT;
}
2020-03-05 16:23:17 +00:00
// Return the enclosed value if this is a number, 0 otherwise. Note that poryjson does not
// distinguish between integer and non-integer numbers - number_value() and int_value()
// can both be applied to a NUMBER-typed object.
double number_value() const;
int int_value() const;
// Return the enclosed value if this is a boolean, false otherwise.
bool bool_value() const;
// Return the enclosed string if this is a string, "" otherwise.
2021-02-18 00:20:14 +00:00
const QString& string_value() const;
2020-03-05 16:23:17 +00:00
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
2021-02-18 00:20:14 +00:00
const array& array_items() const;
2020-03-05 16:23:17 +00:00
// Return the enclosed std::map if this is an object, or an empty map otherwise.
2021-02-18 00:20:14 +00:00
const object& object_items() const;
2020-03-05 16:23:17 +00:00
// Return a reference to arr[i] if this is an array, Json() otherwise.
2021-02-18 00:20:14 +00:00
const Json& operator[](int i) const;
2020-03-05 16:23:17 +00:00
// Return a reference to obj[key] if this is an object, Json() otherwise.
2021-02-18 00:20:14 +00:00
const Json& operator[](const QString& key) const;
2020-03-05 16:23:17 +00:00
// Serialize.
2021-02-18 00:20:14 +00:00
void dump(QString& out, int*) const;
QString dump(int* indent = nullptr) const {
2020-03-05 16:23:17 +00:00
QString out;
if (!indent) {
int temp = 0;
indent = &temp;
}
dump(out, indent);
2020-03-05 16:23:17 +00:00
return out;
}
// Parse. If parse fails, return Json() and assign an error message to err.
2021-02-18 00:20:14 +00:00
static Json parse(const QString& in, QString& err, JsonParse strategy = JsonParse::STANDARD);
static Json parse(const char* in, QString& err, JsonParse strategy = JsonParse::STANDARD) {
2020-03-05 16:23:17 +00:00
if (in) {
return parse(QString(in), err, strategy);
} else {
err = "null input";
return nullptr;
}
}
2021-02-18 00:20:14 +00:00
bool operator==(const Json& rhs) const;
bool operator<(const Json& rhs) const;
bool operator!=(const Json& rhs) const {
return !(*this == rhs);
}
bool operator<=(const Json& rhs) const {
return !(rhs < *this);
}
bool operator>(const Json& rhs) const {
return (rhs < *this);
}
bool operator>=(const Json& rhs) const {
return !(*this < rhs);
}
2020-03-05 16:23:17 +00:00
private:
std::shared_ptr<JsonValue> m_ptr;
};
class JsonDoc {
public:
2021-02-18 00:20:14 +00:00
JsonDoc(Json* object) {
this->m_obj = object;
this->m_indent = 0;
};
2021-02-18 00:20:14 +00:00
void dump(QFile* file) {
QTextStream fileStream(file);
fileStream << m_obj->dump(&m_indent);
fileStream << "\n"; // pad file with newline
}
private:
2021-02-18 00:20:14 +00:00
Json* m_obj;
int m_indent;
};
2020-03-05 16:23:17 +00:00
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
class JsonValue {
protected:
friend class Json;
friend class JsonInt;
friend class JsonDouble;
virtual Json::Type type() const = 0;
2021-02-18 00:20:14 +00:00
virtual bool equals(const JsonValue* other) const = 0;
virtual bool less(const JsonValue* other) const = 0;
virtual void dump(QString& out, int* indent) const = 0;
2020-03-05 16:23:17 +00:00
virtual double number_value() const;
virtual int int_value() const;
virtual bool bool_value() const;
2021-02-18 00:20:14 +00:00
virtual const QString& string_value() const;
virtual const Json::array& array_items() const;
virtual const Json& operator[](int i) const;
virtual const Json::object& object_items() const;
virtual const Json& operator[](const QString& key) const;
virtual ~JsonValue() {
}
2020-03-05 16:23:17 +00:00
};
} // namespace poryjson
#endif // ORDERED_JSON_H