1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
|
#pragma once
#include "StarDataStream.hpp"
#include "StarVariant.hpp"
#include "StarString.hpp"
#include "StarXXHash.hpp"
namespace Star {
STAR_EXCEPTION(JsonException, StarException);
STAR_EXCEPTION(JsonParsingException, StarException);
STAR_CLASS(Json);
typedef List<Json> JsonArray;
typedef shared_ptr<JsonArray const> JsonArrayConstPtr;
typedef StringMap<Json> JsonObject;
typedef shared_ptr<JsonObject const> JsonObjectConstPtr;
// Class for holding representation of JSON data. Immutable and implicitly
// shared.
class Json {
public:
template <typename Container>
struct IteratorWrapper {
typedef typename Container::const_iterator const_iterator;
typedef const_iterator iterator;
const_iterator begin() const;
const_iterator end() const;
shared_ptr<Container const> ptr;
};
enum class Type : uint8_t {
Null = 0,
Float = 1,
Bool = 2,
Int = 3,
String = 4,
Array = 5,
Object = 6
};
static String typeName(Type t);
static Type typeFromName(String const& t);
static Json ofType(Type t);
// Parses JSON or JSON sub-type
static Json parse(String const& string);
// Parses JSON sequence
static Json parseSequence(String const& sequence);
// Parses JSON object or array only (the only top level types allowed by
// JSON)
static Json parseJson(String const& json);
// Constructs type Null
Json();
Json(double);
Json(bool);
Json(int);
Json(long);
Json(long long);
Json(unsigned int);
Json(unsigned long);
Json(unsigned long long);
Json(char const*);
Json(String::Char const*);
Json(String::Char const*, size_t);
Json(String);
Json(std::string);
Json(JsonArray);
Json(JsonObject);
// Float and Int types are convertible between each other. toDouble,
// toFloat, toInt, toUInt may be called on either an Int or a Float. For a
// Float this is simply a C style cast from double, and for an Int it is
// simply a C style cast from int64_t.
//
// Bools, Strings, Arrays, Objects, and Null are not automatically
// convertible to any other type.
double toDouble() const;
float toFloat() const;
bool toBool() const;
int64_t toInt() const;
uint64_t toUInt() const;
String toString() const;
JsonArray toArray() const;
JsonObject toObject() const;
// Internally, String, JsonArray, and JsonObject are shared via shared_ptr
// since this class is immutable. Use these methods to get at this pointer
// without causing a copy.
StringConstPtr stringPtr() const;
JsonArrayConstPtr arrayPtr() const;
JsonObjectConstPtr objectPtr() const;
// As a convenience, make it easy to safely and quickly iterate over a
// JsonArray or JsonObject contents by holding the container pointer.
IteratorWrapper<JsonArray> iterateArray() const;
IteratorWrapper<JsonObject> iterateObject() const;
// opt* methods work like this, if the json is null, it returns none. If the
// json is convertible, it returns the converted type, otherwise an exception
// occurrs.
Maybe<Json> opt() const;
Maybe<double> optDouble() const;
Maybe<float> optFloat() const;
Maybe<bool> optBool() const;
Maybe<int64_t> optInt() const;
Maybe<uint64_t> optUInt() const;
Maybe<String> optString() const;
Maybe<JsonArray> optArray() const;
Maybe<JsonObject> optObject() const;
// Size of array / object type json
size_t size() const;
// If this json is array type, get the value at the given index
Json get(size_t index) const;
double getDouble(size_t index) const;
float getFloat(size_t index) const;
bool getBool(size_t index) const;
int64_t getInt(size_t index) const;
uint64_t getUInt(size_t index) const;
String getString(size_t index) const;
JsonArray getArray(size_t index) const;
JsonObject getObject(size_t index) const;
// These versions of get* return default value if the index is out of range,
// or if the value pointed to is null.
Json get(size_t index, Json def) const;
double getDouble(size_t index, double def) const;
float getFloat(size_t index, float def) const;
bool getBool(size_t index, bool def) const;
int64_t getInt(size_t index, int64_t def) const;
uint64_t getUInt(size_t index, int64_t def) const;
String getString(size_t index, String def) const;
JsonArray getArray(size_t index, JsonArray def) const;
JsonObject getObject(size_t index, JsonObject def) const;
// If object type, whether object contains key
bool contains(String const& key) const;
// If this json is object type, get the value for the given key
Json get(String const& key) const;
double getDouble(String const& key) const;
float getFloat(String const& key) const;
bool getBool(String const& key) const;
int64_t getInt(String const& key) const;
uint64_t getUInt(String const& key) const;
String getString(String const& key) const;
JsonArray getArray(String const& key) const;
JsonObject getObject(String const& key) const;
// These versions of get* return the default if the key is missing or the
// value is null.
Json get(String const& key, Json def) const;
double getDouble(String const& key, double def) const;
float getFloat(String const& key, float def) const;
bool getBool(String const& key, bool def) const;
int64_t getInt(String const& key, int64_t def) const;
uint64_t getUInt(String const& key, int64_t def) const;
String getString(String const& key, String def) const;
JsonArray getArray(String const& key, JsonArray def) const;
JsonObject getObject(String const& key, JsonObject def) const;
// Works the same way as opt methods above. Will never return a null value,
// if there is a null entry it will just return an empty Maybe.
Maybe<Json> opt(String const& key) const;
Maybe<double> optDouble(String const& key) const;
Maybe<float> optFloat(String const& key) const;
Maybe<bool> optBool(String const& key) const;
Maybe<int64_t> optInt(String const& key) const;
Maybe<uint64_t> optUInt(String const& key) const;
Maybe<String> optString(String const& key) const;
Maybe<JsonArray> optArray(String const& key) const;
Maybe<JsonObject> optObject(String const& key) const;
// Combines gets recursively in friendly expressions. For
// example, call like this: json.query("path.to.array[3][4]")
Json query(String const& path) const;
double queryDouble(String const& path) const;
float queryFloat(String const& path) const;
bool queryBool(String const& path) const;
int64_t queryInt(String const& path) const;
uint64_t queryUInt(String const& path) const;
String queryString(String const& path) const;
JsonArray queryArray(String const& path) const;
JsonObject queryObject(String const& path) const;
// These versions of get* do not throw on missing / null keys anywhere in the
// query path.
Json query(String const& path, Json def) const;
double queryDouble(String const& path, double def) const;
float queryFloat(String const& path, float def) const;
bool queryBool(String const& path, bool def) const;
int64_t queryInt(String const& path, int64_t def) const;
uint64_t queryUInt(String const& path, uint64_t def) const;
String queryString(String const& path, String const& def) const;
JsonArray queryArray(String const& path, JsonArray def) const;
JsonObject queryObject(String const& path, JsonObject def) const;
// Returns none on on missing / null keys anywhere in the query path. Will
// never return a null value, just an empty Maybe.
Maybe<Json> optQuery(String const& path) const;
Maybe<double> optQueryDouble(String const& path) const;
Maybe<float> optQueryFloat(String const& path) const;
Maybe<bool> optQueryBool(String const& path) const;
Maybe<int64_t> optQueryInt(String const& path) const;
Maybe<uint64_t> optQueryUInt(String const& path) const;
Maybe<String> optQueryString(String const& path) const;
Maybe<JsonArray> optQueryArray(String const& path) const;
Maybe<JsonObject> optQueryObject(String const& path) const;
// Returns a *new* object with the given values set/erased. Throws if not an
// object.
Json set(String key, Json value) const;
Json setPath(String path, Json value) const;
Json setAll(JsonObject values) const;
Json eraseKey(String key) const;
Json erasePath(String path) const;
// Returns a *new* array with the given values set/inserted/appended/erased.
// Throws if not an array.
Json set(size_t index, Json value) const;
Json insert(size_t index, Json value) const;
Json append(Json value) const;
Json eraseIndex(size_t index) const;
Type type() const;
String typeName() const;
Json convert(Type u) const;
bool isType(Type type) const;
bool canConvert(Type type) const;
// isNull returns true when the type of the Json is null. operator bool() is
// the opposite of isNull().
bool isNull() const;
explicit operator bool() const;
// Prints JSON or JSON sub-type. If sort is true, then any object anywhere
// inside this value will be sorted alphanumerically before being written,
// resulting in a known *unique* textual representation of the Json that is
// cross-platform.
String repr(int pretty = 0, bool sort = false) const;
// Prints JSON object or array only (only top level types allowed by JSON)
String printJson(int pretty = 0, bool sort = false) const;
// Same but avoids quotation marks if this is a string
String printString() const;
// operator== and operator!= compare for exact equality with all types, and
// additionally equality with numeric conversion with Int <-> Float
bool operator==(Json const& v) const;
bool operator!=(Json const& v) const;
// Does this Json not share its storage with any other Json?
bool unique() const;
void getHash(XXHash3& hasher) const;
private:
Json const* ptr(size_t index) const;
Json const* ptr(String const& key) const;
Variant<Empty, double, bool, int64_t, StringConstPtr, JsonArrayConstPtr, JsonObjectConstPtr> m_data;
};
std::ostream& operator<<(std::ostream& os, Json const& v);
// Fixes ambiguity with OrderedHashMap operator<<
std::ostream& operator<<(std::ostream& os, JsonObject const& v);
// Serialize json to DataStream. Strings are stored as UTF-8, ints are stored
// as VLQ, doubles as 64 bit.
DataStream& operator<<(DataStream& ds, Json const& v);
DataStream& operator>>(DataStream& ds, Json& v);
// Convenience methods for Json containers
DataStream& operator<<(DataStream& ds, JsonArray const& l);
DataStream& operator>>(DataStream& ds, JsonArray& l);
DataStream& operator<<(DataStream& ds, JsonObject const& m);
DataStream& operator>>(DataStream& ds, JsonObject& m);
// Merges the two given json values and returns the result, by the following
// rules (applied in order): If the base value is null, returns the merger.
// If the merger value is null, returns base. For any two non-objects types,
// returns the merger. If both values are objects, then the resulting object
// is the combination of both objects, but for each repeated key jsonMerge is
// called recursively on both values to determine the result.
Json jsonMerge(Json const& base, Json const& merger);
// Same as above, but applies null mergers.
Json jsonMergeNulling(Json const& base, Json const& merger);
template <typename... T>
Json jsonMerge(Json const& base, Json const& merger, T const&... rest);
// Similar to jsonMerge, but query only for a single key. Gets a value equal
// to jsonMerge(jsons...).query(key, Json()), but much faster than doing an
// entire merge operation.
template <typename... T>
Json jsonMergeQuery(String const& key, Json const& first, T const&... rest);
// jsonMergeQuery with a default.
template <typename... T>
Json jsonMergeQueryDef(String const& key, Json def, Json const& first, T const&... rest);
template <>
struct hash<Json> {
size_t operator()(Json const& v) const;
};
template <typename Container>
auto Json::IteratorWrapper<Container>::begin() const -> const_iterator {
return ptr->begin();
}
template <typename Container>
auto Json::IteratorWrapper<Container>::end() const -> const_iterator {
return ptr->end();
}
template <typename... T>
Json jsonMerge(Json const& base, Json const& merger, T const&... rest) {
return jsonMerge(jsonMerge(base, merger), rest...);
}
template <typename... T>
Json jsonMergeQuery(String const&, Json def) {
return def;
}
template <typename... T>
Json jsonMergeQueryImpl(String const& key, Json const& json) {
return json.query(key, {});
}
template <typename... T>
Json jsonMergeQueryImpl(String const& key, Json const& base, Json const& first, T const&... rest) {
Json value = jsonMergeQueryImpl(key, first, rest...);
if (value && !value.isType(Json::Type::Object))
return value;
return jsonMerge(base.query(key, {}), value);
}
template <typename... T>
Json jsonMergeQuery(String const& key, Json const& first, T const&... rest) {
return jsonMergeQueryImpl(key, first, rest...);
}
template <typename... T>
Json jsonMergeQueryDef(String const& key, Json def, Json const& first, T const&... rest) {
if (auto v = jsonMergeQueryImpl(key, first, rest...))
return v;
return def;
}
// Compares two JSON values to see if the second is a subset of the first.
// For objects, each key in the second object must exist in the first
// object and the values are recursively compared the same way. For arrays,
// each element in the second array must successfully compare with some
// element of the first array, regardless of order or duplication.
// For all other types, the values must be equal.
bool jsonPartialMatch(Json const& base, Json const& compare);
}
template <> struct fmt::formatter<Star::Json> : ostream_formatter {};
template <> struct fmt::formatter<Star::JsonObject> : ostream_formatter {};
|