OmniSciDB  a987f07e93
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
json.h
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // Heavy.AI's thin JSON library wrapper.
18 
19 // EXAMPLE #1
20 //
21 // #include "Shared/json.h"
22 // using JSON = heavyai::JSON;
23 // JSON json;
24 // json["item1"] = "abc";
25 // json["item2"] = 123;
26 // std::cout << json.stringify() << std::endl;
27 // std::cout << json["item2"].str() << std::endl;
28 //
29 // OUTPUT: {"item1":"abc","item2":123}
30 // OUTPUT: 123
31 
32 // EXAMPLE #2
33 //
34 // #include "Shared/json.h"
35 // using JSON = heavyai::JSON;
36 // std::string text = R"json(
37 // {
38 // "item1": "abc",
39 // "item2": 123
40 // }
41 // )json";
42 // JSON json(text);
43 // json["item3"] = false;
44 // json["item4"].parse("[0, 1, 2, 3, 4]");
45 // std::cout << json.stringify() << std::endl;
46 // std::cout << static_cast<size_t>(json["item4"][2]) << std::endl;
47 //
48 // OUTPUT: {"item1":"abc","item2":123,"item3":false,"item4":[0,1,2,3,4]}
49 // OUTPUT: 2
50 
51 #pragma once
52 
53 #include <rapidjson/document.h>
54 #include <rapidjson/stringbuffer.h>
55 #include <rapidjson/writer.h>
56 #include <limits>
57 #include <memory>
58 #include <stdexcept>
59 #include <string>
60 #include <type_traits>
61 
62 // Calling any public JSON constructor except the move constructor creates a new document.
63 // Calling JSON's operator[] creates a reference into an existing document.
64 
65 namespace heavyai {
66 
67 class JSON final {
68  std::shared_ptr<rapidjson::Document> doc_;
69  rapidjson::Value* vptr_;
70  rapidjson::Document::AllocatorType& allo_;
71  const std::string name_; // only used in error messages
72 
73  public:
74  // Default constructor makes a new empty document.
75  JSON()
76  : doc_(std::make_shared<rapidjson::Document>())
77  , vptr_(&*doc_)
78  , allo_(doc_->GetAllocator())
79  , name_("JSON") {}
80 
81  // Copy constructor to make a new document as a deep copy of the peer document.
82  JSON(const JSON& peer) : JSON() { vptr_->CopyFrom(*peer.vptr_, allo_); }
83 
84  // Move constructor to keep the existing document. Class members are moved efficiently.
85  JSON(JSON&&) = default;
86 
87  // Constructor from std::string to make a new document.
88  JSON(const std::string& json) : JSON() { parse(json); }
89 
90  // Constructor from C-style string to make a new document.
91  JSON(const char* json) : JSON() { parse(json); }
92 
93  // Constructor from C-style string plus a length to make a new document without having
94  // to scan the string for the null terminator.
95  JSON(const char* json, size_t len) : JSON() { parse(json, len); }
96 
97  // parse(): Loads JSON from std::string to make a new document.
98  void parse(const std::string& json) {
99  if (doc_->Parse(json.c_str()).HasParseError()) {
100  throw std::runtime_error("failed to parse json");
101  }
102  }
103 
104  // parse(): Loads JSON from C-style string to make a new document.
105  void parse(const char* json) {
106  if (doc_->Parse(json).HasParseError()) {
107  throw std::runtime_error("failed to parse json");
108  }
109  }
110 
111  // parse(): Loads JSON from C-style string plus a length to make a new document without
112  // having to scan the string for the null terminator.
113  void parse(const char* json, size_t len) {
114  if (doc_->Parse(json, len).HasParseError()) {
115  throw std::runtime_error("failed to parse json");
116  }
117  }
118 
119  // stringify(): Outputs the JSON as a string, even if it had some non-string JSON type.
120  std::string stringify() const {
121  rapidjson::StringBuffer buf;
122  rapidjson::Writer<rapidjson::StringBuffer> wr(buf);
123  vptr_->Accept(wr);
124  return buf.GetString();
125  }
126 
127  // Convienence functions for dynamically retrieving a JSON value as some C++ type.
128  // Will throw an exception if the JSON type doesn't match the requested C++ type.
129  // You can also assign a JSON object to a C++ value or static_cast<>() to a C++ type.
130 
131  std::string str() const { return static_cast<std::string>(*this); }
132 
133  bool b1() const { return static_cast<bool>(*this); }
134 
135  uint64_t u64() const { return static_cast<uint64_t>(*this); }
136  int64_t i64() const { return static_cast<int64_t>(*this); }
137 
138  uint32_t u32() const { return static_cast<uint32_t>(*this); }
139  int32_t i32() const { return static_cast<int32_t>(*this); }
140 
141  uint16_t u16() const { return static_cast<uint16_t>(*this); }
142  int16_t i16() const { return static_cast<int16_t>(*this); }
143 
144  uint8_t u8() const { return static_cast<uint8_t>(*this); }
145  int8_t i8() const { return static_cast<int8_t>(*this); }
146 
147  double d64() const { return static_cast<double>(*this); }
148  float f32() const { return static_cast<float>(*this); }
149 
150  // Functions for detecting a particular JSON type.
151 
152  bool isString() const { return vptr_->IsString(); }
153  bool isNumber() const { return vptr_->IsNumber(); }
154  bool isBoolean() const { return vptr_->IsBool(); }
155  bool isObject() const { return vptr_->IsObject(); }
156  bool isArray() const { return vptr_->IsArray(); }
157  bool isNull() const { return vptr_->IsNull(); }
158 
159  // A function for checking member name existence.
160 
161  bool hasMember(const std::string& name) const { return vptr_->HasMember(name.c_str()); }
162 
163  // Conversion operators for dynamically retrieving a JSON value as some C++ type via
164  // an assignment, passing to a function call, a static_cast<>(), etc.
165 
166  operator std::string() const {
167  if (!vptr_->IsString()) {
168  throw std::runtime_error("expected JSON field '" + name_ +
169  "' to be String but got [" + kTypeNames[vptr_->GetType()] +
170  "]");
171  }
172  return std::string{vptr_->GetString(), vptr_->GetStringLength()};
173  }
174 
175  operator bool() const {
176  if (!vptr_->IsBool()) {
177  throw std::runtime_error("expected JSON field '" + name_ +
178  "' to be Boolean but got [" +
179  kTypeNames[vptr_->GetType()] + "]");
180  }
181  return vptr_->GetBool();
182  }
183 
184  operator uint64_t() const {
185  if (!vptr_->IsUint64()) {
186  throw std::runtime_error("can't convert JSON field '" + name_ +
187  "' to be unsigned 64-bit integer from [" +
188  kTypeNames[vptr_->GetType()] + "]");
189  }
190  return vptr_->GetUint64();
191  }
192 
193  operator int64_t() const {
194  if (!vptr_->IsInt64()) {
195  throw std::runtime_error("can't convert JSON field '" + name_ +
196  "' to be signed 64-bit integer from [" +
197  kTypeNames[vptr_->GetType()] + "]");
198  }
199  return vptr_->GetInt64();
200  }
201 
202  operator uint32_t() const {
203  if (!vptr_->IsUint()) {
204  throw std::runtime_error("can't convert JSON field '" + name_ +
205  "' to be unsigned 32-bit integer from [" +
206  kTypeNames[vptr_->GetType()] + "]");
207  }
208  return vptr_->GetUint();
209  }
210 
211  operator int32_t() const {
212  if (!vptr_->IsInt()) {
213  throw std::runtime_error("can't convert JSON field '" + name_ +
214  "' to be signed 32-bit integer from [" +
215  kTypeNames[vptr_->GetType()] + "]");
216  }
217  return vptr_->GetInt();
218  }
219 
220  operator uint16_t() const {
221  if (!vptr_->IsUint()) {
222  throw std::runtime_error("can't convert JSON field '" + name_ +
223  "' to be unsigned 16-bit integer from [" +
224  kTypeNames[vptr_->GetType()] + "]");
225  }
226  return vptr_->GetUint();
227  }
228 
229  operator int16_t() const {
230  if (!vptr_->IsInt()) {
231  throw std::runtime_error("can't convert JSON field '" + name_ +
232  "' to be signed 16-bit integer from [" +
233  kTypeNames[vptr_->GetType()] + "]");
234  }
235  return vptr_->GetInt();
236  }
237 
238  operator uint8_t() const {
239  if (!vptr_->IsUint()) {
240  throw std::runtime_error("can't convert JSON field '" + name_ +
241  "' to be unsigned 8-bit integer from [" +
242  kTypeNames[vptr_->GetType()] + "]");
243  }
244  return vptr_->GetUint();
245  }
246 
247  operator int8_t() const {
248  if (!vptr_->IsInt()) {
249  throw std::runtime_error("can't convert JSON field '" + name_ +
250  "' to be signed 8-bit integer from [" +
251  kTypeNames[vptr_->GetType()] + "]");
252  }
253  return vptr_->GetInt();
254  }
255 
256  operator double() const {
257  if (!vptr_->IsDouble()) {
258  throw std::runtime_error("can't convert JSON field '" + name_ +
259  "' to be floating point number from [" +
260  kTypeNames[vptr_->GetType()] + "]");
261  }
262  return vptr_->GetDouble();
263  }
264 
265  operator float() const {
266  if (!vptr_->IsDouble()) {
267  throw std::runtime_error("can't convert JSON field '" + name_ +
268  "' to be floating point number from [" +
269  kTypeNames[vptr_->GetType()] + "]");
270  }
271  return static_cast<float>(vptr_->GetDouble());
272  }
273 
274  // Assignment operators.
275 
276  JSON& operator=(const JSON& peer) {
277  vptr_->CopyFrom(*peer.vptr_, allo_);
278  return *this;
279  }
280 
281  JSON& operator=(const std::string& item) {
282  *vptr_ = rapidjson::Value().SetString(item.c_str(), allo_);
283  return *this;
284  }
285 
286  JSON& operator=(const char* item) {
287  *vptr_ = rapidjson::Value().SetString(item, allo_);
288  return *this;
289  }
290 
291  JSON& operator=(bool item) {
292  vptr_->SetBool(item);
293  return *this;
294  }
295 
296  JSON& operator=(int32_t item) {
297  vptr_->SetInt(item);
298  return *this;
299  }
300 
301  JSON& operator=(int64_t item) {
302  vptr_->SetInt64(item);
303  return *this;
304  }
305 
306  JSON& operator=(uint32_t item) {
307  vptr_->SetUint(item);
308  return *this;
309  }
310 
311  JSON& operator=(uint64_t item) {
312  vptr_->SetUint64(item);
313  return *this;
314  }
315 
316  // Array operators.
317 
318  JSON operator[](const std::string& name) { return (*this)[name.c_str()]; }
319  JSON operator[](const char* name) {
320  if (!vptr_->IsObject()) {
321  vptr_->SetObject();
322  }
323  if (!vptr_->HasMember(name)) {
324  vptr_->AddMember(
325  rapidjson::Value(name, allo_).Move(), rapidjson::Value().Move(), allo_);
326  auto f = vptr_->FindMember(name);
327  // f necessary because AddMember inexplicably doesn't return the new member
328  // https://stackoverflow.com/questions/52113291/which-object-reference-does-genericvalueaddmember-return
329  return JSON(doc_, &f->value, allo_, name);
330  }
331  return JSON(doc_, &(*vptr_)[name], allo_, name);
332  }
333 
334  JSON operator[](const std::string& name) const { return (*this)[name.c_str()]; }
335  JSON operator[](const char* name) const {
336  if (!vptr_->IsObject()) {
337  throw std::runtime_error("JSON " + kTypeNames[vptr_->GetType()] + " field '" +
338  name_ + "' can't use operator []");
339  }
340  if (!vptr_->HasMember(name)) {
341  throw std::runtime_error("JSON field '" + std::string(name) + "' not found");
342  }
343  return JSON(doc_, &(*vptr_)[name], allo_, name);
344  }
345 
346  template <typename T>
347  JSON operator[](T index) {
348  return operator[](static_cast<size_t>(index));
349  }
350  JSON operator[](size_t index) {
351  if (!vptr_->IsArray()) {
352  vptr_->SetArray();
353  }
354  if (index >= vptr_->Size()) {
355  throw std::runtime_error("JSON array index " + std::to_string(index) +
356  " out of range " + std::to_string(vptr_->Size()));
357  }
358  return JSON(doc_, &(*vptr_)[index], allo_, std::to_string(index));
359  }
360 
361  template <typename T>
362  JSON operator[](T index) const {
363  return operator[](static_cast<size_t>(index));
364  }
365  JSON operator[](size_t index) const {
366  if (!vptr_->IsArray()) {
367  throw std::runtime_error("JSON " + kTypeNames[vptr_->GetType()] + " field '" +
368  name_ + "' can't use operator []");
369  }
370  if (index >= vptr_->Size()) {
371  throw std::runtime_error("JSON array index " + std::to_string(index) +
372  " out of range " + std::to_string(vptr_->Size()));
373  }
374  return JSON(doc_, &(*vptr_)[index], allo_, std::to_string(index));
375  }
376 
377  private:
378  inline static std::string kTypeNames[] =
379  {"Null", "False", "True", "Object", "Array", "String", "Number"};
380 
381  // Constructor used by operator[] to produce a reference into an existing document.
382  JSON(std::shared_ptr<rapidjson::Document> doc,
383  rapidjson::Value* vptr,
384  rapidjson::Document::AllocatorType& allo,
385  const std::string& name)
386  : doc_(doc), vptr_(vptr), allo_(allo), name_(name) {}
387 
388  friend bool operator==(const JSON& json1, const JSON& json2);
389  friend bool operator!=(const JSON& json1, const JSON& json2);
390 
391  template <typename T>
392  friend bool operator==(const JSON& json, const T& value);
393  template <typename T>
394  friend bool operator==(const T& value, const JSON& json);
395 
396  template <typename T>
397  friend bool operator!=(const JSON& json, const T& value);
398  template <typename T>
399  friend bool operator!=(const T& value, const JSON& json);
400 }; // class JSON
401 
402 // Compare the values referred to by two JSON objects.
403 
404 inline bool operator==(const JSON& json1, const JSON& json2) {
405  return (*json1.vptr_ == *json2.vptr_);
406 }
407 
408 inline bool operator!=(const JSON& json1, const JSON& json2) {
409  return (*json1.vptr_ != *json2.vptr_);
410 }
411 
412 // Compare a JSON object's value with a value of an arbitrary type.
413 
414 template <typename T>
415 inline bool operator==(const JSON& json, const T& value) {
416  return (*json.vptr_ == value);
417 }
418 template <typename T>
419 inline bool operator==(const T& value, const JSON& json) {
420  return (json == value);
421 }
422 
423 template <typename T>
424 inline bool operator!=(const JSON& json, const T& value) {
425  return (*json.vptr_ != value);
426 }
427 template <typename T>
428 inline bool operator!=(const T& value, const JSON& json) {
429  return (json != value);
430 }
431 
432 } // namespace heavyai
JSON(const std::string &json)
Definition: json.h:88
JSON & operator=(const JSON &peer)
Definition: json.h:276
bool b1() const
Definition: json.h:133
std::shared_ptr< rapidjson::Document > doc_
Definition: json.h:68
int32_t i32() const
Definition: json.h:139
bool isNumber() const
Definition: json.h:153
friend bool operator!=(const JSON &json1, const JSON &json2)
Definition: json.h:408
bool isArray() const
Definition: json.h:156
uint16_t u16() const
Definition: json.h:141
JSON operator[](const char *name)
Definition: json.h:319
void parse(const std::string &json)
Definition: json.h:98
int16_t i16() const
Definition: json.h:142
JSON & operator=(uint64_t item)
Definition: json.h:311
JSON & operator=(int64_t item)
Definition: json.h:301
JSON(const char *json, size_t len)
Definition: json.h:95
float f32() const
Definition: json.h:148
JSON & operator=(bool item)
Definition: json.h:291
int8_t i8() const
Definition: json.h:145
uint8_t u8() const
Definition: json.h:144
bool isBoolean() const
Definition: json.h:154
uint64_t u64() const
Definition: json.h:135
constexpr double f
Definition: Utm.h:31
std::string to_string(char const *&&v)
uint32_t u32() const
Definition: json.h:138
JSON operator[](const char *name) const
Definition: json.h:335
JSON & operator=(const std::string &item)
Definition: json.h:281
bool isNull() const
Definition: json.h:157
std::string stringify() const
Definition: json.h:120
JSON & operator=(uint32_t item)
Definition: json.h:306
JSON operator[](T index) const
Definition: json.h:362
bool isString() const
Definition: json.h:152
JSON operator[](T index)
Definition: json.h:347
std::string str() const
Definition: json.h:131
int64_t i64() const
Definition: json.h:136
bool hasMember(const std::string &name) const
Definition: json.h:161
rapidjson::Value * vptr_
Definition: json.h:69
JSON operator[](size_t index) const
Definition: json.h:365
JSON & operator=(const char *item)
Definition: json.h:286
JSON(const char *json)
Definition: json.h:91
JSON operator[](const std::string &name) const
Definition: json.h:334
JSON operator[](size_t index)
Definition: json.h:350
JSON(std::shared_ptr< rapidjson::Document > doc, rapidjson::Value *vptr, rapidjson::Document::AllocatorType &allo, const std::string &name)
Definition: json.h:382
friend bool operator==(const JSON &json1, const JSON &json2)
Definition: json.h:404
JSON(const JSON &peer)
Definition: json.h:82
JSON operator[](const std::string &name)
Definition: json.h:318
rapidjson::Document::AllocatorType & allo_
Definition: json.h:70
bool isObject() const
Definition: json.h:155
double d64() const
Definition: json.h:147
JSON & operator=(int32_t item)
Definition: json.h:296
bool operator==(const JSON &json1, const JSON &json2)
Definition: json.h:404
string name
Definition: setup.in.py:72
bool operator!=(const JSON &json1, const JSON &json2)
Definition: json.h:408
void parse(const char *json, size_t len)
Definition: json.h:113
void parse(const char *json)
Definition: json.h:105
const std::string name_
Definition: json.h:71
static std::string kTypeNames[]
Definition: json.h:378