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