OmniSciDB  c0231cc57d
 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 // OmniSci JSON
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 << static_cast<int>(json["item2"]) << 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  void parse(const std::string& json) {
98  if (doc_->Parse(json).HasParseError()) {
99  throw std::runtime_error("failed to parse json");
100  }
101  }
102 
103  void parse(const char* json) {
104  if (doc_->Parse(json).HasParseError()) {
105  throw std::runtime_error("failed to parse json");
106  }
107  }
108 
109  void parse(const char* json, size_t len) {
110  if (doc_->Parse(json, len).HasParseError()) {
111  throw std::runtime_error("failed to parse json");
112  }
113  }
114 
115  std::string stringify() const {
116  rapidjson::StringBuffer buf;
117  rapidjson::Writer<rapidjson::StringBuffer> wr(buf);
118  vptr_->Accept(wr);
119  return buf.GetString();
120  }
121 
122  [[deprecated]] std::string getType() const { return kTypeNames[vptr_->GetType()]; }
123 
124  bool isString() const { return vptr_->IsString(); }
125  bool isNumber() const { return vptr_->IsNumber(); }
126  bool isBoolean() const { return vptr_->IsBool(); }
127  bool isObject() const { return vptr_->IsObject(); }
128  bool isArray() const { return vptr_->IsArray(); }
129  bool isNull() const { return vptr_->IsNull(); }
130 
131  bool hasMember(const std::string& name) const { return vptr_->HasMember(name); }
132 
133  operator std::string() const {
134  if (!vptr_->IsString()) {
135  throw std::runtime_error("expected JSON field '" + name_ +
136  "' to be String but got [" + kTypeNames[vptr_->GetType()] +
137  "]");
138  }
139  return std::string{vptr_->GetString(), vptr_->GetStringLength()};
140  }
141 
142  operator bool() const {
143  if (!vptr_->IsBool()) {
144  throw std::runtime_error("expected JSON field '" + name_ +
145  "' to be Boolean but got [" +
146  kTypeNames[vptr_->GetType()] + "]");
147  }
148  return vptr_->GetBool();
149  }
150 
151  template <typename T>
152  operator T() const {
153  static_assert((std::is_integral_v<T> && !std::is_same_v<bool, std::remove_cv_t<T>>) ||
154  (std::is_floating_point_v<T>));
155  if constexpr (std::is_integral_v<T>) { // NOLINT
156  if constexpr (std::numeric_limits<T>::is_signed) { // NOLINT
157  if constexpr (sizeof(T) < 8) { // NOLINT
158  if (!vptr_->IsInt()) {
159  throw std::runtime_error("can't convert JSON field '" + name_ +
160  "' to be signed integer from [" +
161  kTypeNames[vptr_->GetType()] + "]");
162  }
163  return vptr_->GetInt();
164  } else {
165  if (!vptr_->IsInt64()) {
166  throw std::runtime_error("can't convert JSON field '" + name_ +
167  "' to be signed 64-bit integer from [" +
168  kTypeNames[vptr_->GetType()] + "]");
169  }
170  return vptr_->GetInt64();
171  }
172  } else {
173  if constexpr (sizeof(T) < 8) { // NOLINT
174  if (!vptr_->IsUint()) {
175  throw std::runtime_error("can't convert JSON field '" + name_ +
176  "' to be unsigned integer from [" +
177  kTypeNames[vptr_->GetType()] + "]");
178  }
179  return vptr_->GetUint();
180  } else {
181  if (!vptr_->IsUint64()) {
182  throw std::runtime_error("can't convert JSON field '" + name_ +
183  "' to be unsigned 64-bit integer from [" +
184  kTypeNames[vptr_->GetType()] + "]");
185  }
186  return vptr_->GetUint64();
187  }
188  }
189  } else if constexpr (std::is_floating_point_v<T>) { // NOLINT
190  if (!vptr_->IsDouble()) {
191  throw std::runtime_error("can't convert JSON field '" + name_ +
192  "' to be floating point number from [" +
193  kTypeNames[vptr_->GetType()] + "]");
194  }
195  return vptr_->GetDouble();
196  }
197  }
198 
199  JSON& operator=(const JSON& peer) {
200  vptr_->CopyFrom(*peer.vptr_, allo_);
201  return *this;
202  }
203 
204  JSON& operator=(const std::string& item) {
205  *vptr_ = rapidjson::Value().SetString(item, allo_);
206  return *this;
207  }
208 
209  JSON& operator=(const char* item) {
210  *vptr_ = rapidjson::Value().SetString(item, allo_);
211  return *this;
212  }
213 
214  JSON& operator=(bool item) {
215  vptr_->SetBool(item);
216  return *this;
217  }
218 
219  JSON& operator=(int32_t item) {
220  vptr_->SetInt(item);
221  return *this;
222  }
223 
224  JSON& operator=(int64_t item) {
225  vptr_->SetInt64(item);
226  return *this;
227  }
228 
229  JSON& operator=(uint32_t item) {
230  vptr_->SetUint(item);
231  return *this;
232  }
233 
234  JSON& operator=(uint64_t item) {
235  vptr_->SetUint64(item);
236  return *this;
237  }
238 
239  JSON operator[](const std::string& name) { return (*this)[name.c_str()]; }
240  JSON operator[](const char* name) {
241  if (!vptr_->IsObject()) {
242  vptr_->SetObject();
243  }
244  if (!vptr_->HasMember(name)) {
245  vptr_->AddMember(
246  rapidjson::Value(name, allo_).Move(), rapidjson::Value().Move(), allo_);
247  auto f = vptr_->FindMember(name);
248  // f necessary because AddMember inexplicably doesn't return the new member
249  // https://stackoverflow.com/questions/52113291/which-object-reference-does-genericvalueaddmember-return
250  return JSON(doc_, &f->value, allo_, name);
251  }
252  return JSON(doc_, &(*vptr_)[name], allo_, name);
253  }
254 
255  JSON operator[](const std::string& name) const { return (*this)[name.c_str()]; }
256  JSON operator[](const char* name) const {
257  if (!vptr_->IsObject()) {
258  throw std::runtime_error("JSON " + kTypeNames[vptr_->GetType()] + " field '" +
259  name_ + "' can't use operator []");
260  }
261  if (!vptr_->HasMember(name)) {
262  throw std::runtime_error("JSON field '" + std::string(name) + "' not found");
263  }
264  return JSON(doc_, &(*vptr_)[name], allo_, name);
265  }
266 
267  template <typename T>
268  JSON operator[](T index) {
269  return operator[](static_cast<size_t>(index));
270  }
271  JSON operator[](size_t index) {
272  if (!vptr_->IsArray()) {
273  vptr_->SetArray();
274  }
275  if (index >= vptr_->Size()) {
276  throw std::runtime_error("JSON array index " + std::to_string(index) +
277  " out of range " + std::to_string(vptr_->Size()));
278  }
279  return JSON(doc_, &(*vptr_)[index], allo_, std::to_string(index));
280  }
281 
282  template <typename T>
283  JSON operator[](T index) const {
284  return operator[](static_cast<size_t>(index));
285  }
286  JSON operator[](size_t index) const {
287  if (!vptr_->IsArray()) {
288  throw std::runtime_error("JSON " + kTypeNames[vptr_->GetType()] + " field '" +
289  name_ + "' can't use operator []");
290  }
291  if (index >= vptr_->Size()) {
292  throw std::runtime_error("JSON array index " + std::to_string(index) +
293  " out of range " + std::to_string(vptr_->Size()));
294  }
295  return JSON(doc_, &(*vptr_)[index], allo_, std::to_string(index));
296  }
297 
298  private:
299  inline static std::string kTypeNames[] =
300  {"Null", "False", "True", "Object", "Array", "String", "Number"};
301 
302  // Constructor used by operator[] to produce a reference into an existing document.
303  JSON(std::shared_ptr<rapidjson::Document> doc,
304  rapidjson::Value* vptr,
305  rapidjson::Document::AllocatorType& allo,
306  const std::string& name)
307  : doc_(doc), vptr_(vptr), allo_(allo), name_(name) {}
308 
309  friend bool operator==(const JSON& json1, const JSON& json2);
310  friend bool operator!=(const JSON& json1, const JSON& json2);
311 
312  template <typename T>
313  friend bool operator==(const JSON& json, const T& value);
314  template <typename T>
315  friend bool operator==(const T& value, const JSON& json);
316 
317  template <typename T>
318  friend bool operator!=(const JSON& json, const T& value);
319  template <typename T>
320  friend bool operator!=(const T& value, const JSON& json);
321 }; // class JSON
322 
323 // Compare the values referred to by two JSON objects.
324 
325 inline bool operator==(const JSON& json1, const JSON& json2) {
326  return (*json1.vptr_ == *json2.vptr_);
327 }
328 
329 inline bool operator!=(const JSON& json1, const JSON& json2) {
330  return (*json1.vptr_ != *json2.vptr_);
331 }
332 
333 // Compare a JSON object's value with a value of an arbitrary type.
334 
335 template <typename T>
336 inline bool operator==(const JSON& json, const T& value) {
337  return (*json.vptr_ == value);
338 }
339 template <typename T>
340 inline bool operator==(const T& value, const JSON& json) {
341  return (json == value);
342 }
343 
344 template <typename T>
345 inline bool operator!=(const JSON& json, const T& value) {
346  return (*json.vptr_ != value);
347 }
348 template <typename T>
349 inline bool operator!=(const T& value, const JSON& json) {
350  return (json != value);
351 }
352 
353 } // namespace heavyai
JSON(const std::string &json)
Definition: json.h:88
JSON & operator=(const JSON &peer)
Definition: json.h:199
std::shared_ptr< rapidjson::Document > doc_
Definition: json.h:68
std::string getType() const
Definition: json.h:122
bool isNumber() const
Definition: json.h:125
friend bool operator!=(const JSON &json1, const JSON &json2)
Definition: json.h:329
bool isArray() const
Definition: json.h:128
JSON operator[](const char *name)
Definition: json.h:240
void parse(const std::string &json)
Definition: json.h:97
JSON & operator=(uint64_t item)
Definition: json.h:234
JSON & operator=(int64_t item)
Definition: json.h:224
JSON(const char *json, size_t len)
Definition: json.h:95
JSON & operator=(bool item)
Definition: json.h:214
bool isBoolean() const
Definition: json.h:126
constexpr double f
Definition: Utm.h:31
std::string to_string(char const *&&v)
JSON operator[](const char *name) const
Definition: json.h:256
JSON & operator=(const std::string &item)
Definition: json.h:204
bool isNull() const
Definition: json.h:129
std::string stringify() const
Definition: json.h:115
JSON & operator=(uint32_t item)
Definition: json.h:229
JSON operator[](T index) const
Definition: json.h:283
bool isString() const
Definition: json.h:124
JSON operator[](T index)
Definition: json.h:268
bool hasMember(const std::string &name) const
Definition: json.h:131
rapidjson::Value * vptr_
Definition: json.h:69
JSON operator[](size_t index) const
Definition: json.h:286
JSON & operator=(const char *item)
Definition: json.h:209
JSON(const char *json)
Definition: json.h:91
JSON operator[](const std::string &name) const
Definition: json.h:255
JSON operator[](size_t index)
Definition: json.h:271
JSON(std::shared_ptr< rapidjson::Document > doc, rapidjson::Value *vptr, rapidjson::Document::AllocatorType &allo, const std::string &name)
Definition: json.h:303
friend bool operator==(const JSON &json1, const JSON &json2)
Definition: json.h:325
JSON(const JSON &peer)
Definition: json.h:82
JSON operator[](const std::string &name)
Definition: json.h:239
rapidjson::Document::AllocatorType & allo_
Definition: json.h:70
bool isObject() const
Definition: json.h:127
JSON & operator=(int32_t item)
Definition: json.h:219
bool operator==(const JSON &json1, const JSON &json2)
Definition: json.h:325
string name
Definition: setup.in.py:72
bool operator!=(const JSON &json1, const JSON &json2)
Definition: json.h:329
void parse(const char *json, size_t len)
Definition: json.h:109
void parse(const char *json)
Definition: json.h:103
const std::string name_
Definition: json.h:71
static std::string kTypeNames[]
Definition: json.h:299