jubilant-funicular
Json.cpp
1 #include <algorithm>
2 #include <unordered_set>
3 #include <fstream>
4 
5 #include "Json.h"
6 
7 using namespace std;
8 
9 namespace utils {
10  Json::Json(const initializer_list<Json>& data) : m_type(NONE) {
11  if (data.begin() == data.end()) return;
12  bool is_object = all_of(data.begin(), data.end(), [](const Json& elem) {
13  return elem.is_array() && elem.size() == 2 && elem[0].is_string();
14  });
15  if (is_object) {
16  m_type = OBJECT;
17  m_obj = new JsonObject;
18  for (auto& pair : data) {
19  (*m_obj)[pair[0].as_string()] = pair[1];
20  }
21  } else {
22  m_type = ARRAY;
23  m_arr = new JsonArray(data.begin(), data.end());
24  }
25  }
26  Json::Json(const Json& other) {
27  operator=(other);
28  }
29  Json::Json(Json&& other) {
30  operator=(move(other));
31  }
32  Json& Json::operator=(const Json& other) {
33  m_type = other.m_type;
34  switch(other.m_type) {
35  case STRING:
36  m_str = strdup(other.m_str);
37  break;
38  case NUMBER:
39  m_num = other.m_num;
40  break;
41  case OBJECT:
42  m_obj = new JsonObject(other.m_obj->begin(), other.m_obj->end());
43  break;
44  case ARRAY:
45  m_arr = new JsonArray(other.m_arr->begin(), other.m_arr->end());
46  break;
47  case BOOLEAN:
48  m_bool = other.m_bool;
49  break;
50  }
51  return *this;
52  }
53  Json& Json::operator=(Json&& other) {
54  m_type = other.m_type;
55  switch(other.m_type) {
56  case STRING:
57  m_str = other.m_str;
58  other.m_str = nullptr;
59  break;
60  case NUMBER:
61  m_num = other.m_num;
62  break;
63  case OBJECT:
64  m_obj = other.m_obj;
65  other.m_obj = nullptr;
66  break;
67  case ARRAY:
68  m_arr = other.m_arr;
69  other.m_arr = nullptr;
70  break;
71  case BOOLEAN:
72  m_bool = other.m_bool;
73  break;
74  }
75  other.m_type = NONE;
76  return *this;
77  }
78  Json::~Json() {
79  switch(m_type) {
80  case STRING: free(m_str); break;
81  case OBJECT: if (m_obj) delete m_obj; break;
82  case ARRAY: if (m_arr) delete m_arr; break;
83  }
84  }
85  Json& Json::operator[](const std::string& key) {
86  if (m_type == NONE) {
87  m_type = OBJECT;
88  m_obj = new JsonObject;
89  }
91  return (*m_obj)[key];
92  }
93  Json& Json::operator[](const std::string& key) const {
95  return (*m_obj)[key];
96  }
97  Json& Json::operator[](size_t idx) {
98  if (m_type == NONE) {
99  m_type = ARRAY;
100  m_arr = new JsonArray;
101  }
104  return (*m_arr)[idx];
105  }
106  Json& Json::operator[](size_t idx) const {
109  return (*m_arr)[idx];
110  }
111  size_t Json::size() const {
112  switch(m_type) {
113  case OBJECT: return m_obj->size();
114  case ARRAY: return m_arr->size();
115  default: return 0;
116  }
117  }
118  bool Json::has_key(const std::string& key) const {
119  return m_type == OBJECT ? m_obj->find(key) != m_obj->end() : false;
120  }
121  bool Json::resize(size_t size) {
122  if (m_type == NONE) {
123  m_type = ARRAY;
124  m_arr = new JsonArray;
125  }
126  switch(m_type) {
127  case ARRAY: m_arr->resize(size);
128  }
129  return m_type == ARRAY;
130  }
131  bool Json::push_back(const Json& val) {
132  if (m_type == NONE) {
133  m_type = ARRAY;
134  m_arr = new JsonArray;
135  }
136  if (m_type == ARRAY) m_arr->push_back(val);
137  return m_type == ARRAY;
138  }
139  Json& Json::front() {
140  switch(m_type) {
141  case OBJECT: return m_obj->begin()->second;
142  case ARRAY: return m_arr->front();
143  default: return *this;
144  }
145  }
146  Json& Json::back() {
147  switch(m_type) {
148  case OBJECT: return m_obj->rbegin()->second;
149  case ARRAY: return m_arr->back();
150  default: return *this;
151  }
152  }
153  Json& Json::merge(const Json& other) {
154  if (m_type != other.m_type) return *this;
155  switch(m_type) {
156  case OBJECT:
157  m_obj->insert(other.m_obj->begin(), other.m_obj->end());
158  break;
159  case ARRAY:
160  m_arr->insert(m_arr->end(), other.m_arr->begin(), other.m_arr->end());
161  break;
162  }
163  return *this;
164  }
165  std::string Json::as_string() const {
166  switch(m_type) {
167  case STRING: return m_str;
168  case NUMBER: return m_num.to_string();
169  case BOOLEAN: return m_bool ? "true" : "false";
170  case NONE: return "null";
171  }
172  return "";
173  }
174  JsonNum Json::as_number() const {
175  switch(m_type) {
176  case NUMBER: return m_num;
177  case BOOLEAN: return m_bool;
178  }
179  return 0;
180  }
181  bool Json::as_bool() const {
182  switch(m_type) {
183  case NONE: return false;
184  case BOOLEAN: return m_bool;
185  default: return true;
186  }
187  }
189  std::string Json::dump(size_t indent, size_t offset) const {
190  switch(m_type) {
191  case STRING: return "\"" + as_string() + "\"";
192  case NUMBER: return m_num.dump();
193  case OBJECT: {
194  if (is_empty()) return indent > 0 ? "{\n}" : "{}";
195  std::string ret = "{";
196  std::string offset_str(offset, ' ');
197  std::string indent_str(offset + indent, ' ');
198 
199  for (auto it = cbegin(); it != cend(); ++it) {
200  ret += indent == 0 ? "" : "\n" + indent_str;
201  ret += "\"" + it.key() + "\": " +
202  it.value().dump(indent, offset+indent) + ", ";
203  }
204  ret.replace(ret.size()-2, 2, indent == 0 ? "}" :
205  "\n" + offset_str + "}");
206  return ret;
207  }
208  case ARRAY: {
209  if (is_empty()) return "[]";
210  std::string ret = "[";
211  for (auto it = cbegin(); it != cend(); ++it) {
212  ret += it->dump(indent, offset) + ", ";
213  }
214  ret.replace(ret.size()-2, 2, "]");
215  return ret;
216  }
217  case BOOLEAN: return m_bool ? "true" : "false";
218  case NONE: return "null";
219  }
220  }
221  bool Json::lex_string(std::string& str, JsonToken& ret) {
222  if (str[0] != '\"' || str.size() < 2) return false;
223  int pos = 0;
224  while (pos == 0 || str[pos-1] == '\\') {
225  if (pos != 0) str.replace(pos-1, 1, "");
226  pos = str.find('\"', pos+1);
227  if (pos == std::string::npos) return false;
228  }
229  ret.type = SYMBOL;
230  ret.str = strdup(str.substr(0, pos+1).c_str());
231  str = str.substr(pos+1);
232  return true;
233  }
234  bool Json::lex_number(std::string& str, JsonToken& ret) {
235  static const unordered_set<char> FLOAT_CHARS{
236  '.', 'e', 'E'
237  };
238 
239  char* end;
240  long vall = strtol(str.c_str(), &end, 10);
241  if (end != str.c_str() && FLOAT_CHARS.find(*end) == FLOAT_CHARS.end()) {
242  ret.type = NUMTKN;
243  ret.num = vall >= 0 ? JsonNum((uint64_t)vall) : JsonNum((int64_t)vall);
244  str = str.substr(end - str.c_str());
245  return true;
246  }
247 
248  double vald = strtod(str.c_str(), &end);
249  if (end != str.c_str()) {
250  ret.type = NUMTKN;
251  ret.num = JsonNum(vald);
252  str = str.substr(end - str.c_str());
253  return true;
254  }
255 
256  return false;
257  }
258  bool Json::lex_bool(std::string& str, JsonToken& ret) {
259  if (str.size() >= 4 && str.substr(0, 4) == "true") {
260  ret.type = SYMBOL;
261  ret.str = strdup("true");
262  str = str.substr(4);
263  return true;
264  } if (str.size() >= 5 && str.substr(0, 5) == "false") {
265  ret.type = SYMBOL;
266  ret.str = strdup("false");
267  str = str.substr(5);
268  return true;
269  }
270  return false;
271  }
272  bool Json::lex_null(std::string& str, JsonToken& ret) {
273  if (str.size() >= 4 && str.substr(0, 4) == "null") {
274  ret.type = SYMBOL;
275  ret.str = strdup("null");
276  str = str.substr(4);
277  return true;
278  }
279  return false;
280  }
281  queue<Json::JsonToken> Json::tokenize(std::string curr) {
282  static const unordered_set<char> JSON_SYNTAX{
283  '{', '}', '[', ']', ':', ','
284  };
285 
286  queue<JsonToken> tokens;
287  while (!curr.empty()) {
288  JsonToken tkn;
289  if (lex_string(curr, tkn)) {
290  tokens.push(tkn);
291  } else if (lex_number(curr, tkn)) {
292  tokens.push(tkn);
293  } else if (lex_bool(curr, tkn)) {
294  tokens.push(tkn);
295  } else if (lex_null(curr, tkn)) {
296  tokens.push(tkn);
297  } else if (isspace(curr[0])) {
298  curr = curr.substr(1);
299  } else if (JSON_SYNTAX.find(curr[0]) != JSON_SYNTAX.end()) {
300  tokens.emplace(curr[0]);
301  curr = curr.substr(1);
302  } else {
304  assert(false && "Invalid JSON token");
305  }
306  }
307  return tokens;
308  }
309  Json Json::parse_tokens(queue<JsonToken>& tokens) {
310  if (tokens.empty()) return Json::null();
311  JsonToken tkn = tokens.front();
312  tokens.pop();
313  if (tkn.type == NUMTKN) {
314  return Json::number(tkn.num);
315  } else if (strcmp(tkn.str, "{") == 0) {
316  Json obj;
317  while (tokens.front() != JsonToken('}')) {
318  Json key = parse_tokens(tokens);
319  if (!key.is_string()) return Json::null();
320  if (tokens.front() != JsonToken(':')) return Json::null();
321  tokens.pop();
322 
323  obj[key.as_string()] = parse_tokens(tokens);
324  if (tokens.front() == JsonToken(',')) tokens.pop();
325  }
326  tokens.pop();
327  return obj;
328  } else if (strcmp(tkn.str, "[") == 0) {
329  Json arr;
330  while (tokens.front() != JsonToken(']')) {
331  arr.push_back(parse_tokens(tokens));
332  if (tokens.front() == JsonToken(',')) tokens.pop();
333  }
334  tokens.pop();
335  return arr;
336  } else if (starts_with(tkn.str, "\"")) {
337  int len = strlen(tkn.str);
338  tkn.str[len-1] = '\0';
339  return Json::string(tkn.str+1);
340  } else if (strcmp(tkn.str, "true") == 0) {
341  return Json::boolean(true);
342  } else if (strcmp(tkn.str, "false") == 0) {
343  return Json::boolean(false);
344  } else if (strcmp(tkn.str, "null") == 0) {
345  return Json::null();
346  } else {
347  assert(false && "Invalid JSON syntax");
348  }
349  }
350  Json Json::parse(const std::string& json) {
351  if (json.empty()) return Json::null();
352  if (isspace(json[0])) return parse(trim(json));
353 
354  auto tokens = tokenize(json);
355  return parse_tokens(tokens);
356  }
357  Json Json::from_file(const std::string& path) {
358  std::string contents;
359 
361  ifstream file(path.c_str(), ios::binary);
362  if (file.fail()) return Json::null();
363  file.seekg(0, ios::end);
364  unsigned int file_size = file.tellg();
365  file.seekg(0, ios::beg);
366  file_size -= file.tellg();
367 
368  contents.resize(file_size);
369  file.read((char*)&contents[0], file_size);
370  file.close();
371 
372  return Json::parse(contents);
373  }
374  bool Json::operator==(const Json& other) const {
375  if (m_type != other.m_type) return false;
376  switch(m_type) {
377  case STRING: return strcmp(m_str, other.m_str) == 0;
378  case NUMBER: return m_num == other.m_num;
379  case OBJECT: return m_obj ? (other.m_obj && *m_obj == *other.m_obj) : !other.m_obj;
380  case ARRAY: return m_arr ? (other.m_arr && *m_arr == *other.m_arr) : !other.m_arr;
381  case BOOLEAN: return m_bool == other.m_bool;
382  case NONE: return true;
383  }
384  }
385 }
utils::Json::push_back
bool push_back(const Json &val)
Definition: Json.cpp:131
utils::JsonNum
Definition: Json.h:25
utils::Json::JsonToken
Used for tokenizing strings before parsing them into Json objects.
Definition: Json.h:223
utils::Json
Definition: Json.h:93