1#pragma once
2
3#include <typeinfo>
4#include <Poco/Exception.h>
5#include <common/StringRef.h>
6#include <common/Types.h>
7
8
9/** Очень простой класс для чтения JSON (или его кусочков).
10 * Представляет собой ссылку на кусок памяти, в котором содержится JSON (или его кусочек).
11 * Не создаёт никаких структур данных в оперативке. Не выделяет память (кроме std::string).
12 * Не парсит JSON до конца (парсит только часть, необходимую для выполнения вызванного метода).
13 * Парсинг необходимой части запускается каждый раз при вызове методов.
14 * Может работать с обрезанным JSON-ом.
15 * При этом, (в отличие от SAX-подобных парсеров), предоставляет удобные методы для работы.
16 *
17 * Эта структура данных более оптимальна, если нужно доставать несколько элементов из большого количества маленьких JSON-ов.
18 * То есть, подходит для обработки "параметров визитов" и "параметров интернет магазинов" в Яндекс.Метрике.
19 * Если нужно много работать с одним большим JSON-ом, то этот класс может быть менее оптимальным.
20 *
21 * Имеются следующие соглашения:
22 * 1. Предполагается, что в JSON-е нет пробельных символов.
23 * 2. Предполагается, что строки в JSON в кодировке UTF-8; также могут использоваться \u-последовательности.
24 * Строки возвращаются в кодировке UTF-8, \u-последовательности переводятся в UTF-8.
25 * 3. Но суррогатная пара из двух \uXXXX\uYYYY переводится не в UTF-8, а в CESU-8.
26 * 4. Корректный JSON парсится корректно.
27 * При работе с некорректным JSON-ом, кидается исключение или возвращаются неверные результаты.
28 * (пример: считается, что если встретился символ 'n', то после него идёт 'ull' (null);
29 * если после него идёт ',1,', то исключение не кидается, и, таким образом, возвращается неверный результат)
30 * 5. Глубина вложенности JSON ограничена (см. MAX_JSON_DEPTH в cpp файле).
31 * При необходимости спуститься на большую глубину, кидается исключение.
32 * 6. В отличие от JSON, пользоволяет парсить значения вида 64-битное число, со знаком, или без.
33 * При этом, если число дробное - то дробная часть тихо отбрасывается.
34 * 7. Числа с плавающей запятой парсятся не с максимальной точностью.
35 *
36 * Подходит только для чтения JSON, модификация не предусмотрена.
37 * Все методы immutable, кроме operator++.
38 */
39
40
41POCO_DECLARE_EXCEPTION(Foundation_API, JSONException, Poco::Exception)
42
43
44class JSON
45{
46private:
47 using Pos = const char *;
48 Pos ptr_begin;
49 Pos ptr_end;
50 unsigned level;
51
52public:
53 JSON(Pos ptr_begin_, Pos ptr_end_, unsigned level_ = 0) : ptr_begin(ptr_begin_), ptr_end(ptr_end_), level(level_)
54 {
55 checkInit();
56 }
57
58 JSON(const std::string & s) : ptr_begin(s.data()), ptr_end(s.data() + s.size()), level(0)
59 {
60 checkInit();
61 }
62
63 JSON(const JSON & rhs)
64 {
65 *this = rhs;
66 }
67
68 JSON & operator=(const JSON & rhs)
69 {
70 ptr_begin = rhs.ptr_begin;
71 ptr_end = rhs.ptr_end;
72 level = rhs.level;
73 return *this;
74 }
75
76 const char * data() const { return ptr_begin; }
77 const char * dataEnd() const { return ptr_end; }
78
79 enum ElementType
80 {
81 TYPE_OBJECT,
82 TYPE_ARRAY,
83 TYPE_NUMBER,
84 TYPE_STRING,
85 TYPE_BOOL,
86 TYPE_NULL,
87 TYPE_NAME_VALUE_PAIR,
88 TYPE_NOTYPE,
89 };
90
91 ElementType getType() const;
92
93 bool isObject() const { return getType() == TYPE_OBJECT; }
94 bool isArray() const { return getType() == TYPE_ARRAY; }
95 bool isNumber() const { return getType() == TYPE_NUMBER; }
96 bool isString() const { return getType() == TYPE_STRING; }
97 bool isBool() const { return getType() == TYPE_BOOL; }
98 bool isNull() const { return getType() == TYPE_NULL; }
99 bool isNameValuePair() const { return getType() == TYPE_NAME_VALUE_PAIR; }
100
101 /// Количество элементов в массиве или объекте; если элемент - не массив или объект, то исключение.
102 size_t size() const;
103
104 /// Является ли массив или объект пустыми; если элемент - не массив или объект, то исключение.
105 bool empty() const;
106
107 /// Получить элемент массива по индексу; если элемент - не массив, то исключение.
108 JSON operator[] (size_t n) const;
109
110 /// Получить элемент объекта по имени; если элемент - не объект, то исключение.
111 JSON operator[] (const std::string & name) const;
112
113 /// Есть ли в объекте элемент с заданным именем; если элемент - не объект, то исключение.
114 bool has(const std::string & name) const { return has(name.data(), name.size()); }
115 bool has(const char * data, size_t size) const;
116
117 /// Получить значение элемента; исключение, если элемент имеет неправильный тип.
118 template <class T>
119 T get() const;
120
121 /// если значения нет, или тип неверный, то возвращает дефолтное значение
122 template <class T>
123 T getWithDefault(const std::string & key, const T & default_ = T()) const;
124
125 double getDouble() const;
126 Int64 getInt() const; /// Отбросить дробную часть.
127 UInt64 getUInt() const; /// Отбросить дробную часть. Если число отрицательное - исключение.
128 std::string getString() const;
129 bool getBool() const;
130 std::string getName() const; /// Получить имя name-value пары.
131 JSON getValue() const; /// Получить значение name-value пары.
132
133 StringRef getRawString() const;
134 StringRef getRawName() const;
135
136 /// Получить значение элемента; если элемент - строка, то распарсить значение из строки; если не строка или число - то исключение.
137 double toDouble() const;
138 Int64 toInt() const;
139 UInt64 toUInt() const;
140
141 /** Преобразовать любой элемент в строку.
142 * Для строки возвращается её значение, для всех остальных элементов - сериализованное представление.
143 */
144 std::string toString() const;
145
146 /// Класс JSON одновременно является итератором по самому себе.
147 using iterator = JSON;
148 using const_iterator = JSON;
149
150 iterator operator* () const { return *this; }
151 const JSON * operator-> () const { return this; }
152 bool operator== (const JSON & rhs) const { return ptr_begin == rhs.ptr_begin; }
153 bool operator!= (const JSON & rhs) const { return ptr_begin != rhs.ptr_begin; }
154
155 /** Если элемент - массив или объект, то begin() возвращает iterator,
156 * который указывает на первый элемент массива или первую name-value пару объекта.
157 */
158 iterator begin() const;
159
160 /** end() - значение, которое нельзя использовать; сигнализирует о том, что элементы закончились.
161 */
162 iterator end() const;
163
164 /// Перейти к следующему элементу массива или следующей name-value паре объекта.
165 iterator & operator++();
166 iterator operator++(int);
167
168 /// Есть ли в строке escape-последовательности
169 bool hasEscapes() const;
170
171 /// Есть ли в строке спец-символы из набора \, ', \0, \b, \f, \r, \n, \t, возможно, заэскейпленные.
172 bool hasSpecialChars() const;
173
174private:
175 /// Проверить глубину рекурсии, а также корректность диапазона памяти.
176 void checkInit() const;
177 /// Проверить, что pos лежит внутри диапазона памяти.
178 void checkPos(Pos pos) const;
179
180 /// Вернуть позицию после заданного элемента.
181 Pos skipString() const;
182 Pos skipNumber() const;
183 Pos skipBool() const;
184 Pos skipNull() const;
185 Pos skipNameValuePair() const;
186 Pos skipObject() const;
187 Pos skipArray() const;
188
189 Pos skipElement() const;
190
191 /// Найти name-value пару с заданным именем в объекте.
192 Pos searchField(const std::string & name) const { return searchField(name.data(), name.size()); }
193 Pos searchField(const char * data, size_t size) const;
194
195 template <class T>
196 bool isType() const;
197};
198
199template <class T>
200T JSON::getWithDefault(const std::string & key, const T & default_) const
201{
202 if (has(key))
203 {
204 JSON key_json = (*this)[key];
205
206 if (key_json.isType<T>())
207 return key_json.get<T>();
208 else
209 return default_;
210 }
211 else
212 return default_;
213}
214