2015-09-29 19:21:02 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <typeinfo>
|
|
|
|
|
#include <Poco/Exception.h>
|
2017-06-23 20:22:35 +00:00
|
|
|
|
#include <common/StringRef.h>
|
2017-04-16 05:40:17 +00:00
|
|
|
|
#include <common/Types.h>
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Очень простой класс для чтения JSON (или его кусочков).
|
|
|
|
|
* Представляет собой ссылку на кусок памяти, в котором содержится JSON (или его кусочек).
|
|
|
|
|
* Не создаёт никаких структур данных в оперативке. Не выделяет память (кроме std::string).
|
|
|
|
|
* Не парсит JSON до конца (парсит только часть, необходимую для выполнения вызванного метода).
|
|
|
|
|
* Парсинг необходимой части запускается каждый раз при вызове методов.
|
|
|
|
|
* Может работать с обрезанным JSON-ом.
|
2017-03-31 16:00:30 +00:00
|
|
|
|
* При этом, (в отличие от SAX-подобных парсеров), предоставляет удобные методы для работы.
|
2015-09-29 19:21:02 +00:00
|
|
|
|
*
|
|
|
|
|
* Эта структура данных более оптимальна, если нужно доставать несколько элементов из большого количества маленьких JSON-ов.
|
|
|
|
|
* То есть, подходит для обработки "параметров визитов" и "параметров интернет магазинов" в Яндекс.Метрике.
|
|
|
|
|
* Если нужно много работать с одним большим JSON-ом, то этот класс может быть менее оптимальным.
|
|
|
|
|
*
|
|
|
|
|
* Имеются следующие соглашения:
|
|
|
|
|
* 1. Предполагается, что в JSON-е нет пробельных символов.
|
|
|
|
|
* 2. Предполагается, что строки в JSON в кодировке UTF-8; также могут использоваться \u-последовательности.
|
|
|
|
|
* Строки возвращаются в кодировке UTF-8, \u-последовательности переводятся в UTF-8.
|
|
|
|
|
* 3. Но суррогатная пара из двух \uXXXX\uYYYY переводится не в UTF-8, а в CESU-8.
|
|
|
|
|
* 4. Корректный JSON парсится корректно.
|
|
|
|
|
* При работе с некорректным JSON-ом, кидается исключение или возвращаются неверные результаты.
|
|
|
|
|
* (пример: считается, что если встретился символ 'n', то после него идёт 'ull' (null);
|
|
|
|
|
* если после него идёт ',1,', то исключение не кидается, и, таким образом, возвращается неверный результат)
|
|
|
|
|
* 5. Глубина вложенности JSON ограничена (см. MAX_JSON_DEPTH в cpp файле).
|
|
|
|
|
* При необходимости спуститься на большую глубину, кидается исключение.
|
|
|
|
|
* 6. В отличие от JSON, пользоволяет парсить значения вида 64-битное число, со знаком, или без.
|
|
|
|
|
* При этом, если число дробное - то дробная часть тихо отбрасывается.
|
|
|
|
|
* 7. Числа с плавающей запятой парсятся не с максимальной точностью.
|
|
|
|
|
*
|
|
|
|
|
* Подходит только для чтения JSON, модификация не предусмотрена.
|
|
|
|
|
* Все методы immutable, кроме operator++.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2018-08-10 04:02:56 +00:00
|
|
|
|
POCO_DECLARE_EXCEPTION(Foundation_API, JSONException, Poco::Exception)
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JSON
|
|
|
|
|
{
|
|
|
|
|
private:
|
2017-04-01 07:20:54 +00:00
|
|
|
|
using Pos = const char *;
|
|
|
|
|
Pos ptr_begin;
|
|
|
|
|
Pos ptr_end;
|
|
|
|
|
unsigned level;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
|
JSON(Pos ptr_begin_, Pos ptr_end_, unsigned level_ = 0) : ptr_begin(ptr_begin_), ptr_end(ptr_end_), level(level_)
|
|
|
|
|
{
|
|
|
|
|
checkInit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSON(const std::string & s) : ptr_begin(s.data()), ptr_end(s.data() + s.size()), level(0)
|
|
|
|
|
{
|
|
|
|
|
checkInit();
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 02:06:15 +00:00
|
|
|
|
JSON(const JSON & rhs)
|
|
|
|
|
{
|
|
|
|
|
*this = rhs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSON & operator=(const JSON & rhs)
|
|
|
|
|
{
|
|
|
|
|
ptr_begin = rhs.ptr_begin;
|
|
|
|
|
ptr_end = rhs.ptr_end;
|
|
|
|
|
level = rhs.level;
|
2019-07-08 02:06:37 +00:00
|
|
|
|
return *this;
|
2019-07-08 02:06:15 +00:00
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
|
|
const char * data() const { return ptr_begin; }
|
|
|
|
|
const char * dataEnd() const { return ptr_end; }
|
|
|
|
|
|
|
|
|
|
enum ElementType
|
|
|
|
|
{
|
|
|
|
|
TYPE_OBJECT,
|
|
|
|
|
TYPE_ARRAY,
|
|
|
|
|
TYPE_NUMBER,
|
|
|
|
|
TYPE_STRING,
|
|
|
|
|
TYPE_BOOL,
|
|
|
|
|
TYPE_NULL,
|
|
|
|
|
TYPE_NAME_VALUE_PAIR,
|
|
|
|
|
TYPE_NOTYPE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ElementType getType() const;
|
|
|
|
|
|
2018-08-10 04:02:56 +00:00
|
|
|
|
bool isObject() const { return getType() == TYPE_OBJECT; }
|
|
|
|
|
bool isArray() const { return getType() == TYPE_ARRAY; }
|
|
|
|
|
bool isNumber() const { return getType() == TYPE_NUMBER; }
|
|
|
|
|
bool isString() const { return getType() == TYPE_STRING; }
|
|
|
|
|
bool isBool() const { return getType() == TYPE_BOOL; }
|
|
|
|
|
bool isNull() const { return getType() == TYPE_NULL; }
|
|
|
|
|
bool isNameValuePair() const { return getType() == TYPE_NAME_VALUE_PAIR; }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
|
|
/// Количество элементов в массиве или объекте; если элемент - не массив или объект, то исключение.
|
|
|
|
|
size_t size() const;
|
|
|
|
|
|
|
|
|
|
/// Является ли массив или объект пустыми; если элемент - не массив или объект, то исключение.
|
|
|
|
|
bool empty() const;
|
|
|
|
|
|
|
|
|
|
/// Получить элемент массива по индексу; если элемент - не массив, то исключение.
|
|
|
|
|
JSON operator[] (size_t n) const;
|
|
|
|
|
|
|
|
|
|
/// Получить элемент объекта по имени; если элемент - не объект, то исключение.
|
|
|
|
|
JSON operator[] (const std::string & name) const;
|
|
|
|
|
|
|
|
|
|
/// Есть ли в объекте элемент с заданным именем; если элемент - не объект, то исключение.
|
|
|
|
|
bool has(const std::string & name) const { return has(name.data(), name.size()); }
|
|
|
|
|
bool has(const char * data, size_t size) const;
|
|
|
|
|
|
|
|
|
|
/// Получить значение элемента; исключение, если элемент имеет неправильный тип.
|
|
|
|
|
template <class T>
|
|
|
|
|
T get() const;
|
|
|
|
|
|
|
|
|
|
/// если значения нет, или тип неверный, то возвращает дефолтное значение
|
|
|
|
|
template <class T>
|
|
|
|
|
T getWithDefault(const std::string & key, const T & default_ = T()) const;
|
|
|
|
|
|
2019-01-04 12:10:00 +00:00
|
|
|
|
double getDouble() const;
|
|
|
|
|
Int64 getInt() const; /// Отбросить дробную часть.
|
|
|
|
|
UInt64 getUInt() const; /// Отбросить дробную часть. Если число отрицательное - исключение.
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::string getString() const;
|
2019-01-04 12:10:00 +00:00
|
|
|
|
bool getBool() const;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::string getName() const; /// Получить имя name-value пары.
|
|
|
|
|
JSON getValue() const; /// Получить значение name-value пары.
|
|
|
|
|
|
|
|
|
|
StringRef getRawString() const;
|
|
|
|
|
StringRef getRawName() const;
|
|
|
|
|
|
|
|
|
|
/// Получить значение элемента; если элемент - строка, то распарсить значение из строки; если не строка или число - то исключение.
|
2019-01-04 12:10:00 +00:00
|
|
|
|
double toDouble() const;
|
|
|
|
|
Int64 toInt() const;
|
|
|
|
|
UInt64 toUInt() const;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
|
|
/** Преобразовать любой элемент в строку.
|
|
|
|
|
* Для строки возвращается её значение, для всех остальных элементов - сериализованное представление.
|
|
|
|
|
*/
|
|
|
|
|
std::string toString() const;
|
|
|
|
|
|
|
|
|
|
/// Класс JSON одновременно является итератором по самому себе.
|
|
|
|
|
using iterator = JSON;
|
|
|
|
|
using const_iterator = JSON;
|
|
|
|
|
|
|
|
|
|
iterator operator* () const { return *this; }
|
|
|
|
|
const JSON * operator-> () const { return this; }
|
|
|
|
|
bool operator== (const JSON & rhs) const { return ptr_begin == rhs.ptr_begin; }
|
|
|
|
|
bool operator!= (const JSON & rhs) const { return ptr_begin != rhs.ptr_begin; }
|
|
|
|
|
|
|
|
|
|
/** Если элемент - массив или объект, то begin() возвращает iterator,
|
|
|
|
|
* который указывает на первый элемент массива или первую name-value пару объекта.
|
|
|
|
|
*/
|
|
|
|
|
iterator begin() const;
|
|
|
|
|
|
|
|
|
|
/** end() - значение, которое нельзя использовать; сигнализирует о том, что элементы закончились.
|
|
|
|
|
*/
|
|
|
|
|
iterator end() const;
|
|
|
|
|
|
|
|
|
|
/// Перейти к следующему элементу массива или следующей name-value паре объекта.
|
|
|
|
|
iterator & operator++();
|
|
|
|
|
iterator operator++(int);
|
|
|
|
|
|
|
|
|
|
/// Есть ли в строке escape-последовательности
|
|
|
|
|
bool hasEscapes() const;
|
|
|
|
|
|
|
|
|
|
/// Есть ли в строке спец-символы из набора \, ', \0, \b, \f, \r, \n, \t, возможно, заэскейпленные.
|
|
|
|
|
bool hasSpecialChars() const;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2017-04-01 07:20:54 +00:00
|
|
|
|
/// Проверить глубину рекурсии, а также корректность диапазона памяти.
|
|
|
|
|
void checkInit() const;
|
|
|
|
|
/// Проверить, что pos лежит внутри диапазона памяти.
|
|
|
|
|
void checkPos(Pos pos) const;
|
|
|
|
|
|
|
|
|
|
/// Вернуть позицию после заданного элемента.
|
|
|
|
|
Pos skipString() const;
|
|
|
|
|
Pos skipNumber() const;
|
|
|
|
|
Pos skipBool() const;
|
|
|
|
|
Pos skipNull() const;
|
|
|
|
|
Pos skipNameValuePair() const;
|
|
|
|
|
Pos skipObject() const;
|
|
|
|
|
Pos skipArray() const;
|
|
|
|
|
|
|
|
|
|
Pos skipElement() const;
|
|
|
|
|
|
|
|
|
|
/// Найти name-value пару с заданным именем в объекте.
|
|
|
|
|
Pos searchField(const std::string & name) const { return searchField(name.data(), name.size()); }
|
|
|
|
|
Pos searchField(const char * data, size_t size) const;
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
bool isType() const;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
T JSON::getWithDefault(const std::string & key, const T & default_) const
|
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
|
if (has(key))
|
|
|
|
|
{
|
|
|
|
|
JSON key_json = (*this)[key];
|
|
|
|
|
|
|
|
|
|
if (key_json.isType<T>())
|
|
|
|
|
return key_json.get<T>();
|
|
|
|
|
else
|
|
|
|
|
return default_;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return default_;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
}
|