AFLOW
 
Loading...
Searching...
No Matches
aurostd_xparser_json.cpp
Go to the documentation of this file.
1// ***************************************************************************
2// * *
3// * Aflow STEFANO CURTAROLO - Duke University 2003-2024 *
4// * *
5// ***************************************************************************
6// This JSON class is the evolution of different prior solutions to integrate JSON with the AFLOW source base.
7// hagen.eckert@duke.edu
8
9#ifndef _AUROSTD_XPARSER_JSON_CPP_
10#define _AUROSTD_XPARSER_JSON_CPP_
11
13
14#include <cmath>
15#include <codecvt>
16#include <cstddef>
17#include <cstdlib>
18#include <cstring>
19#include <iomanip>
20#include <ios>
21#include <limits>
22#include <locale>
23#include <map>
24#include <memory>
25#include <sstream>
26#include <string>
27#include <utility>
28#include <vector>
29
30#include "aurostd.h"
31#include "aurostd_xerror.h"
32#include "aurostd_xfile.h"
33#include "aurostd_xscalar.h"
34
35namespace aurostd {
36
46
56 JSON::object &JSON::object::operator[](const size_t index) const {
57 if (this->type == JSON::object_types::LIST) {
58 const std::shared_ptr<JSON::List> content = std::static_pointer_cast<JSON::List>(this->obj);
59 return content->operator[](index);
60 } else {
62 }
63 }
64
74 JSON::object &JSON::object::operator[](const std::string &key) const {
76 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(this->obj);
77 return content->operator[](key);
78 } else {
80 }
81 }
82
85 ostream &operator<<(ostream &os, const JSON::object &jo) {
86 os << jo.toString();
87 return os;
88 }
89
91 JSON::object &JSON::object::operator[](const char *key) const {
93 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(this->obj);
94 return content->operator[](key);
95 } else {
97 }
98 }
99
107 std::string JSON::object::toString(const bool json_format, const bool escape_unicode) const {
108 bool first = true;
109 stringstream result;
110 switch (type) {
112 result << "{";
113 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
114 for (const auto &entry : *content) {
115 if (first) {
116 first = false;
117 } else {
118 result << ",";
119 }
120 result << "\"" << entry.first << "\":" << entry.second; //
121 }
122 result << "}";
123 } break;
124 case object_types::LIST: {
125 result << "[";
126 const std::shared_ptr<JSON::List> content = std::static_pointer_cast<JSON::List>(obj);
127 for (const auto &entry : *content) {
128 if (first) {
129 first = false;
130 } else {
131 result << ",";
132 }
133 result << entry; //
134 }
135 result << "]";
136 } break;
138 const std::shared_ptr<std::string> content = std::static_pointer_cast<std::string>(obj);
139 if (json_format) {
140 result << "\"" << JSON::escape(*content, escape_unicode) << "\"";
141 } else {
142 return *content;
143 }
144 } break;
146 const std::shared_ptr<long long int> content = std::static_pointer_cast<long long int>(obj);
147 result << *content;
148 } break;
149 case object_types::FLOAT: {
150 const std::shared_ptr<double> content = std::static_pointer_cast<double>(obj);
151 result << *content;
152 } break;
153 case object_types::T: {
154 result << "true";
155 } break;
156 case object_types::F: {
157 result << "false";
158 } break;
159 case object_types::NONE: {
160 result << "null";
161 } break;
162 default: break;
163 }
164 return result.str();
165 }
166
173 void JSON::object::saveFile(const std::string &file_path, const compression_type ct, const bool escape_unicode) const {
174 aurostd::string2file(this->toString(true, escape_unicode), file_path, ct);
175 }
176
180 void JSON::object::fromString(const std::string &content) {
181 const std::shared_ptr<std::string> new_string = std::make_shared<std::string>();
182 *new_string = content;
183 this->obj = new_string;
185 }
186
190 void JSON::object::fromList(const std::vector<JSON::object> &content) {
191 const std::shared_ptr<JSON::List> new_list = std::make_shared<JSON::List>();
192 *new_list = content;
193 this->obj = new_list;
194 this->type = object_types::LIST;
195 }
196
200 void JSON::object::fromDictionary(const std::map<std::string, JSON::object> &content) {
201 const std::shared_ptr<JSON::Dictionary> new_dict = std::make_shared<JSON::Dictionary>();
202 *new_dict = content;
203 this->obj = new_dict;
205 }
206
208 JSON::object::object(const char *content) {
209 this->fromString(content);
210 }
211
213 JSON::object::object(const std::string &content) {
214 this->fromString(content);
215 }
216
218 JSON::object::object(bool content) {
219 if (content) {
220 this->type = object_types::T;
221 } else {
222 this->type = object_types::F;
223 }
224 }
225
228 JSON::object::object(std::nullptr_t content) {
229 this->type = object_types::NONE;
230 this->obj = content;
231 }
232
237 this->type = create_type;
238 switch (create_type) {
239 {
241 const std::shared_ptr<JSON::Dictionary> content = std::make_shared<JSON::Dictionary>();
242 this->obj = content;
243 break;
244 }
245 case object_types::LIST: {
246 const std::shared_ptr<JSON::List> content = std::make_shared<JSON::List>();
247 this->obj = content;
248 break;
249 }
251 const std::shared_ptr<std::string> content = std::make_shared<std::string>();
252 this->obj = content;
253 break;
254 }
256 const std::shared_ptr<long long int> content = std::make_shared<long long int>();
257 this->obj = content;
258 break;
259 }
260 case object_types::FLOAT: {
261 const std::shared_ptr<double> content = std::make_shared<double>();
262 this->obj = content;
263 break;
264 }
265 default: {
266 this->obj = nullptr;
267 }
268 }
269 }
270 }
271
272 JSON::object::object(const std::vector<JSON::object> &content) {
273 this->fromList(content);
274 }
275
276 JSON::object::object(const std::map<std::string, JSON::object> &content) {
277 this->fromMap(content);
278 }
279
281 JSON::object &JSON::object::operator=(const char *content) {
282 this->fromString(content);
283 return *this;
284 }
285
287 JSON::object &JSON::object::operator=(const std::string &content) {
288 this->fromString(content);
289 return *this;
290 }
291
294 if (content) {
295 this->type = object_types::T;
296 } else {
297 this->type = object_types::F;
298 }
299 return *this;
300 }
301
303 JSON::object &JSON::object::operator=(std::nullptr_t content) {
304 this->type = object_types::NONE;
305 this->obj = content;
306 return *this;
307 }
308
311 this->fromList(content);
312 return *this;
313 }
314
317 this->fromDictionary(content);
318 return *this;
319 }
320
324 JSON::object::operator bool() const {
325 switch (type) {
326 {
328 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
329 if (content->empty()) {
330 return false;
331 } else {
332 return true;
333 }
334 }
335 case object_types::LIST: {
336 const std::shared_ptr<JSON::List> content = std::static_pointer_cast<JSON::List>(obj);
337 if (content->empty()) {
338 return false;
339 } else {
340 return true;
341 }
342 }
344 const std::shared_ptr<std::string> content = std::static_pointer_cast<std::string>(obj);
345 if (content->empty()) {
346 return false;
347 } else {
348 return true;
349 }
350 }
352 const std::shared_ptr<long long int> content = std::static_pointer_cast<long long int>(obj);
353 if (*content) {
354 return true;
355 } else {
356 return false;
357 }
358 }
359 case object_types::FLOAT: {
360 const std::shared_ptr<double> content = std::static_pointer_cast<double>(obj);
361 if (static_cast<bool>(*content)) {
362 return true;
363 } else {
364 return false;
365 }
366 }
367 case object_types::T: {
368 return true;
369 }
370 case object_types::F: {
371 return false;
372 }
373 case object_types::NONE: {
374 return false;
375 }
376 default: {
377 return false;
378 }
379 }
380 }
381 }
382
384 JSON::object::operator double() const {
385 switch (type) {
386 {
388 const std::shared_ptr<long long int> content = std::static_pointer_cast<long long int>(obj);
389 return (double) *content;
390 }
391 case object_types::FLOAT: {
392 const std::shared_ptr<double> content = std::static_pointer_cast<double>(obj);
393 return *content;
394 }
395 case object_types::T: return 1.0;
396 case object_types::F: return 0.0;
397 case object_types::NONE: return NAN;
398 default: throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON double conversion failed: is not a number: " + static_cast<std::string>(*this), _VALUE_ILLEGAL_);
399 }
400 }
401 }
402
404 JSON::object::operator long double() const {
405 return static_cast<double>(*this);
406 }
407
409 JSON::object::operator float() const {
410 return static_cast<double>(*this);
411 }
412
414 JSON::object::operator long long() const {
415 switch (type) {
416 {
418 const std::shared_ptr<long long int> content = std::static_pointer_cast<long long int>(obj);
419 return *content;
420 }
421 case object_types::FLOAT: {
422 const std::shared_ptr<double> content = std::static_pointer_cast<double>(obj);
423 if (*content > static_cast<double>(std::numeric_limits<long long>::max())) {
424 return std::numeric_limits<long long>::max();
425 }
426 if (*content < static_cast<double>(std::numeric_limits<long long>::lowest())) {
427 return std::numeric_limits<long long>::lowest();
428 }
429 return static_cast<long long>(*content);
430 }
431 case object_types::T: return 1;
432 case object_types::F: return 0;
433 default: throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON long long conversion failed: element is not a number: " + static_cast<std::string>(*this), _VALUE_ILLEGAL_);
434 }
435 }
436 }
437
439 JSON::object::operator unsigned long long() const {
440 const long long result = static_cast<long long>(*this);
441 if (result >= 0) {
442 return result;
443 } else {
444 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON unsigned long long conversion failed: element is not a positive number: " + static_cast<std::string>(*this), _VALUE_ILLEGAL_);
445 }
446 }
447
449 JSON::object::operator unsigned long() const {
450 return static_cast<unsigned long long>(*this);
451 }
452
454 JSON::object::operator unsigned int() const {
455 return static_cast<unsigned long long>(*this);
456 }
457
459 JSON::object::operator long() const {
460 return static_cast<long long>(*this);
461 }
462
464 JSON::object::operator int() const {
465 return static_cast<long long>(*this);
466 }
467
469 JSON::object::operator char() const {
470 return static_cast<long long>(*this);
471 }
472
474 JSON::object::operator std::string() const {
475 return this->toString(false, false);
476 }
477
479 JSON::object::operator std::vector<object>() const {
480 if (type != object_types::LIST) {
481 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON std::vector conversion failed: element is not a LIST: " + static_cast<std::string>(*this), _VALUE_ILLEGAL_);
482 }
483 const std::shared_ptr<JSON::List> content = std::static_pointer_cast<JSON::List>(obj);
484 return *content;
485 }
486
488 JSON::object::operator std::map<std::string, JSON::object>() const {
489 if (type != object_types::DICTIONARY) {
490 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON std::map conversion failed: element is not a DICTIONARY: " + static_cast<std::string>(*this), _VALUE_ILLEGAL_);
491 }
492 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
493 return *content;
494 }
495
497 void JSON::object::push_back(const JSON::object &content) const {
499 const std::shared_ptr<JSON::List> list_obj = std::static_pointer_cast<JSON::List>(obj);
500 list_obj->push_back(content);
501 } else {
502 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "push_back is just allowed for a JSON LIST", _VALUE_ILLEGAL_);
503 }
504 }
505
508 void JSON::object::join(const JSON::object &content) const {
510 const std::shared_ptr<JSON::Dictionary> dict_obj = std::static_pointer_cast<JSON::Dictionary>(obj);
511 const std::shared_ptr<JSON::Dictionary> dict_content = std::static_pointer_cast<JSON::Dictionary>(content.obj);
512 for (auto &[key, value] : *dict_content) {
513 dict_obj->operator[](key) = value;
514 }
515 } else if (type == JSON::object_types::LIST and content.type == JSON::object_types::LIST) {
516 const std::shared_ptr<JSON::List> list_obj = std::static_pointer_cast<JSON::List>(obj);
517 const std::shared_ptr<JSON::List> list_content = std::static_pointer_cast<JSON::List>(content.obj);
518 for (auto &it : *list_content) {
519 list_obj->push_back(it);
520 }
521 } else {
522 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "insert is just allowed for a JSON DICTIONARY and LIST", _VALUE_ILLEGAL_);
523 }
524 }
525
527 size_t JSON::object::size() const {
528 switch (type) {
530 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
531 return content->size();
532 }
533 case object_types::LIST: {
534 const std::shared_ptr<JSON::List> content = std::static_pointer_cast<JSON::List>(obj);
535 return content->size();
536 }
538 const std::shared_ptr<std::string> content = std::static_pointer_cast<std::string>(obj);
539 return content->size();
540 }
541 default: {
542 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "size is just allowed for a JSON DICTIONARY, LIST, or STRING", _VALUE_ILLEGAL_);
543 }
544 }
545 }
546
548 bool JSON::object::empty() const {
550 return not static_cast<bool>(*this);
551 }
552 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "empty is just allowed for a JSON DICTIONARY, LIST, or STRING", _VALUE_ILLEGAL_);
553 }
554
556 size_t JSON::object::count(const std::string &key) const {
557 switch (type) {
559 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
560 return content->count(key);
561 }
562 default: {
563 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "count is just allowed for a JSON DICTIONARY", _VALUE_ILLEGAL_);
564 }
565 }
566 }
567
569 std::map<std::string, JSON::object>::iterator JSON::object::end() const {
570 switch (type) {
572 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
573 return content->end();
574 }
575 default: {
576 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "end is just allowed for a JSON DICTIONARY", _VALUE_ILLEGAL_);
577 }
578 }
579 }
580
582 std::map<std::string, JSON::object>::iterator JSON::object::begin() const {
583 switch (type) {
585 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
586 return content->begin();
587 }
588 default: {
589 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "begin is just allowed for a JSON DICTIONARY", _VALUE_ILLEGAL_);
590 }
591 }
592 }
593
595 std::map<std::string, JSON::object>::iterator JSON::object::find(const std::string &key) const {
596 switch (type) {
598 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
599 return content->find(key);
600 }
601 default: {
602 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "find is just allowed for a JSON DICTIONARY", _VALUE_ILLEGAL_);
603 }
604 }
605 }
606
608 std::vector<std::string> JSON::object::keys() const {
609 switch (type) {
611 const std::shared_ptr<JSON::Dictionary> content = std::static_pointer_cast<JSON::Dictionary>(obj);
612 std::vector<std::string> keys;
613 keys.reserve(content->size());
614 for (const auto &[key, value] : *content) {
615 keys.push_back(key);
616 }
617 return keys;
618 }
619 default: {
620 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "keys is just allowed for a JSON DICTIONARY", _VALUE_ILLEGAL_);
621 }
622 }
623 }
624
625} // namespace aurostd
626
627namespace aurostd {
680
686 static inline size_t range_find(const char *content_ptr, const std::pair<size_t, size_t> &border, const char to_find) {
687 return static_cast<const char *>(memchr(content_ptr + border.first, to_find, border.second - border.first + 1)) - content_ptr;
688 }
689
696 std::string JSON::parse_string(const std::string &raw_content, std::pair<size_t, size_t> border) {
697 if (border.second == 0) {
698 border.second = raw_content.size() - 1;
699 }
700 size_t last_escape_pos = border.first;
701 size_t current_escape_pos = range_find(raw_content.c_str(), border, '\\');
702
703 if (current_escape_pos > border.second) {
704 return raw_content.substr(border.first, border.second - border.first + 1);
705 }
706
707 std::string result;
708 while (current_escape_pos <= border.second) {
709 result += raw_content.substr(last_escape_pos, current_escape_pos - last_escape_pos);
710 switch (raw_content[current_escape_pos + 1]) {
711 case '"': {
712 result += '"';
713 } break;
714 case '\\': {
715 result += '\\';
716 } break;
717 case '/': {
718 result += '/';
719 } break;
720 case 'b': {
721 result += '\b';
722 } break;
723 case 'f': {
724 result += '\f';
725 } break;
726 case 'n': {
727 result += '\n';
728 } break;
729 case 'r': {
730 result += '\r';
731 } break;
732 case 't': {
733 result += '\t';
734 } break;
735 case 'u': {
736 if (current_escape_pos + 6 > raw_content.size()) {
737 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: undefined unicode character", _FILE_WRONG_FORMAT_);
738 }
739 result += unescape_unicode(raw_content, current_escape_pos);
740 current_escape_pos += 2;
741 } break;
742 default: {
743 stringstream message;
744 message << "JSON parsing failed: string contains undefined escape '\\" << raw_content[current_escape_pos + 1] << "'";
746 }
747 }
748 last_escape_pos = current_escape_pos + 2;
749 current_escape_pos = range_find(raw_content.c_str(), {last_escape_pos, border.second}, '\\');
750 }
751 result += raw_content.substr(last_escape_pos, border.second - last_escape_pos + 1);
752 return result;
753 }
754
756 std::string JSON::char_escape(const char16_t c) {
757 switch (c) {
758 default: return "";
759 case ('"'): return "\\\"";
760 case ('\\'): return "\\\\";
761 case ('/'): return "\\/";
762 case ('\b'): return "\\b";
763 case ('\f'): return "\\f";
764 case ('\n'): return "\\n";
765 case ('\r'): return "\\r";
766 case ('\t'): return "\\t";
767 }
768 }
769
776 std::string JSON::escape(const std::string &raw, const bool unicode) {
777 std::stringstream out;
778 if (unicode) {
779 const std::u16string utf16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(raw.data());
780 for (const char16_t c : utf16) {
781 if (c < 0x80) {
782 if (char_escape(c).empty()) {
783 out << static_cast<char>(c);
784 } else {
785 out << char_escape(c);
786 }
787 } else {
788 out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << c;
789 }
790 }
791 } else {
792 for (const char16_t c : raw) {
793 if (char_escape(c).empty()) {
794 out << static_cast<char>(c);
795 } else {
796 out << char_escape(c);
797 }
798 }
799 }
800 return out.str();
801 }
802
808 std::string JSON::char32_to_string(const char32_t cp) {
809 string out;
810 if (cp < 0x80) {
811 out += static_cast<char>(cp);
812 return out;
813 }
814 if (cp < 0x800) {
815 out += static_cast<char>((cp >> 6) | 0xc0);
816 out += static_cast<char>((cp & 0x3f) | 0x80);
817 return out;
818 }
819 if (cp < 0x10000) {
820 out += static_cast<char>((cp >> 12) | 0xe0);
821 out += static_cast<char>(((cp >> 6) & 0x3f) | 0x80);
822 out += static_cast<char>((cp & 0x3f) | 0x80);
823 return out;
824 }
825
826 out += static_cast<char>((cp >> 18) | 0xf0);
827 out += static_cast<char>(((cp >> 12) & 0x3f) | 0x80);
828 out += static_cast<char>(((cp >> 6) & 0x3f) | 0x80);
829 out += static_cast<char>((cp & 0x3f) | 0x80);
830 return out;
831 }
832
840 std::string JSON::unescape_unicode(const std::string &raw, size_t &pos) {
841 pos += 2;
842 const char32_t cp = (char32_t) aurostd::string2utype<uint>(raw.substr(pos, 4), 16);
843
844 if (cp < 0xd800 || cp > 0xdfff) {
845 return char32_to_string(cp);
846 } else if (cp > 0xdbff) {
847 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: undefined unicode character", _FILE_WRONG_FORMAT_);
848 } else {
849 if (raw[pos + 4] == '\\' and raw[pos + 5] == 'u') {
850 pos += 6;
851 const char32_t trailing_cp = static_cast<char32_t>(aurostd::string2utype<uint>(raw.substr(pos, 4), 16));
852 if (trailing_cp < 0xdc00 || trailing_cp > 0xdfff) {
853 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: undefined unicode character", _FILE_WRONG_FORMAT_);
854 }
855 const char32_t combo_cp = ((cp - 0xd800) << 10) + (trailing_cp - 0xdc00) + 0x10000;
856 return char32_to_string(combo_cp);
857 } else {
858 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: undefined unicode character", _FILE_WRONG_FORMAT_);
859 }
860 }
861 }
862
869 std::pair<size_t, size_t> JSON::find_strip(const std::string &raw_content, std::pair<size_t, size_t> border) {
870 if (border.second == 0) {
871 border.second = raw_content.size() - 1;
872 }
873 const size_t start = raw_content.find_first_not_of(" \n\t\r\v\f", border.first);
874 const size_t end = raw_content.find_last_not_of(" \n\t\r\v\f", border.second);
875 return {start, min(end, border.second)};
876 }
877
884 std::pair<size_t, size_t> JSON::find_string(const std::string &raw_content, std::pair<size_t, size_t> border) {
885 if (border.second == 0) {
886 border.second = raw_content.size() - 1;
887 }
888 const size_t start = range_find(raw_content.c_str(), border, '"');
889 if (start >= border.second) {
890 return {std::string::npos, std::string::npos};
891 }
892 size_t end = start;
893 uint escape_check = 0;
894 size_t escape_check_pos = 0;
895 if (start == std::string::npos) {
896 return {start, end};
897 }
898 do {
899 end = range_find(raw_content.c_str(), {end + 1, border.second}, '"');
900 escape_check_pos = end - 1;
901 escape_check = 0;
902 while (raw_content[escape_check_pos] == '\\' and escape_check_pos >= start) {
903 escape_check++;
904 escape_check_pos--;
905 }
906 } while ((escape_check % 2 != 0) && (end <= border.second));
907 if (end > border.second) {
908 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: string not closed", _FILE_WRONG_FORMAT_);
909 }
910 return {start, end};
911 }
912
920 std::pair<size_t, size_t> JSON::find_bracket(const std::string &raw_content, const char kind_open, std::pair<size_t, size_t> border) {
921 char kind_close;
922 switch (kind_open) {
923 case '[': kind_close = ']'; break;
924 case '{': kind_close = '}'; break;
925 case '(': kind_close = ')'; break;
926 default: throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: bracket kind not allowed", _FILE_WRONG_FORMAT_);
927 }
928 const size_t start = raw_content.find(kind_open, border.first);
929 size_t end = start;
930 if (start > border.second) {
931 return {std::string::npos, std::string::npos};
932 }
933 size_t next_open = 0;
934 size_t next_close = 0;
935 std::pair<size_t, size_t> string_section;
936 size_t open_count = 1;
937 do {
938 // cerr <<"end: " << end << " | " << "open: " << open_count << " | current selection: " << raw_content.substr(start, end-start+1) << endl; // detailed debug
939 string_section = find_string(raw_content, {end + 1, border.second});
940 next_close = range_find(raw_content.c_str(), {end + 1, border.second}, kind_close);
941 next_open = range_find(raw_content.c_str(), {end + 1, border.second}, kind_open);
942 if (next_close > string_section.first && next_open > string_section.first) {
943 end = string_section.second;
944 continue;
945 }
946 if (next_close < next_open) {
947 if (next_close < string_section.first) {
948 end = next_close;
949 open_count--;
950 }
951 } else if (next_close > next_open) {
952 if (next_open < string_section.first) {
953 end = next_open;
954 open_count++;
955 }
956 } else { // when == std::string::npos
957 break;
958 }
959
960 } while (open_count > 0 && (end < border.second));
961
962 return {start, end};
963 }
964
971 JSON::object JSON::parse(const std::string &raw_content, std::pair<size_t, size_t> border) {
972 if (border.second == 0) {
973 border.second = raw_content.size() - 1;
974 }
975 object result;
976 result.type = object_types::NONE;
977 result.obj = nullptr;
978 border = find_strip(raw_content, border);
979 std::pair<size_t, size_t> section({0, 0});
980 switch (raw_content[border.first]) {
981 case '{': {
982 if (raw_content[border.second] != '}') {
983 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: object not closed by '}'", _FILE_WRONG_FORMAT_);
984 }
985 const std::shared_ptr<JSON::Dictionary> new_dictionary = std::make_shared<JSON::Dictionary>();
986 border = {border.first + 1, border.second - 1};
987 section = {0, 0};
988 do {
989 border = find_strip(raw_content, border);
990 // cout << border.first << " | "<< border.second << " - current: " << raw_content.substr(border.first, border.second-border.first+1) << endl; // detailed debug
991 if (border.first >= border.second) {
992 break;
993 }
994 section = find_string(raw_content, border);
995 if (section.first == std::string::npos) {
996 break;
997 }
998 const std::string key_string = parse_string(raw_content, {section.first + 1, section.second - 1}); // cut " already
999 // cout << "key: " << key_string << endl;
1000 border.first = raw_content.find(':', section.second) + 1;
1001 border = find_strip(raw_content, border);
1002 // cout << border.first << " | "<< border.second << " - switch: " << raw_content.substr(border.first, border.second-border.first+1) << endl; // detailed debug
1003
1004 switch (raw_content[border.first]) {
1005 case '"': {
1006 section = find_string(raw_content, border);
1007 } break;
1008 case '[': {
1009 section = find_bracket(raw_content, '[', border);
1010 } break;
1011 case '{': {
1012 section = find_bracket(raw_content, '{', border);
1013 } break;
1014 default: {
1015 section.first = border.first;
1016 section.second = min(border.second, range_find(raw_content.c_str(), border, ',') - 1);
1017 }
1018 }
1019 new_dictionary->insert({key_string, parse(raw_content, section)});
1020 border.first = range_find(raw_content.c_str(), {section.second, border.second}, ',');
1021 if (border.first == std::string::npos) {
1022 border.first = border.second;
1023 } else {
1024 border.first++;
1025 }
1026 } while (border.first < border.second);
1027 result.obj = new_dictionary;
1029 } break;
1030 case '[': {
1031 if (raw_content[border.second] != ']') {
1032 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: list not closed by ']'", _FILE_WRONG_FORMAT_);
1033 }
1034 const std::shared_ptr<JSON::List> new_list = std::make_shared<JSON::List>();
1035 border = {border.first + 1, border.second - 1};
1036 section = {0, 0};
1037 do {
1038 border = find_strip(raw_content, border);
1039 switch (raw_content[border.first]) {
1040 case '"': {
1041 section = find_string(raw_content, border);
1042 } break;
1043 case '[': {
1044 section = find_bracket(raw_content, '[', border);
1045 } break;
1046 case '{': {
1047 section = find_bracket(raw_content, '{', border);
1048 } break;
1049 default: {
1050 section.first = border.first;
1051 section.second = min(border.second, raw_content.find(',', border.first) - 1);
1052 }
1053 }
1054 new_list->emplace_back(parse(raw_content, section));
1055 border.first = raw_content.find(',', section.second);
1056 if (border.first == std::string::npos) {
1057 border.first = border.second;
1058 } else {
1059 border.first++;
1060 }
1061 } while (section.second < border.second);
1062 result.obj = new_list;
1063 result.type = object_types::LIST;
1064 } break;
1065 case '"': {
1066 if (raw_content[border.second] != '"') {
1067 throw aurostd::xerror(__AFLOW_FILE__, __AFLOW_FUNC__, "JSON parsing failed: string not enclosed by '\"'", _FILE_WRONG_FORMAT_);
1068 }
1069 const std::shared_ptr<std::string> new_string = std::make_shared<std::string>();
1070 *new_string = parse_string(raw_content, {border.first + 1, border.second - 1});
1071 result.obj = new_string;
1072 result.type = object_types::STRING;
1073 } break;
1074 case 'n': {
1075 result.obj = nullptr;
1076 result.type = object_types::NONE;
1077 } break;
1078 case 't': {
1079 result.obj = nullptr;
1080 result.type = object_types::T;
1081 } break;
1082 case 'f': {
1083 result.obj = nullptr;
1084 result.type = object_types::F;
1085 } break;
1086 default: { // number
1087 if (raw_content.find('.', border.first) != std::string::npos || raw_content.find('E', border.first) != std::string::npos || raw_content.find('e', border.first) != std::string::npos) {
1088 const std::shared_ptr<double> new_number = std::make_shared<double>();
1089 *new_number = std::strtod(raw_content.c_str() + border.first, nullptr);
1090 result.obj = new_number;
1091 result.type = object_types::FLOAT;
1092 } else {
1093 const std::shared_ptr<long long> new_number = std::make_shared<long long>();
1094 *new_number = std::strtoll(raw_content.c_str() + border.first, nullptr, 10);
1095 result.obj = new_number;
1096 result.type = object_types::INTEGER;
1097 }
1098 }
1099 }
1100 return result;
1101 }
1102
1106 JSON::object JSON::loadFile(const std::string &file_path) {
1107 const std::string raw_content = aurostd::file2string(file_path);
1108 return parse(raw_content);
1109 }
1110
1114 JSON::object JSON::loadString(const std::string &content) {
1115 return parse(content);
1116 }
1117
1122 std::string JSON::toString(const object &root, const bool escape_unicode) {
1123 return root.toString(true, escape_unicode);
1124 }
1125
1130 void JSON::saveFile(const object &root, const std::string &file_path, const compression_type ct, const bool escape_unicode) {
1131 aurostd::string2file(root.toString(true, escape_unicode), file_path, ct);
1132 }
1133
1134} // namespace aurostd
1135
1136#endif // _AUROSTD_XPARSER_JSON_CPP_
unsigned uint
Definition aurostd.h:39
#define __AFLOW_FILE__
Definition aurostd.h:44
#define __AFLOW_FUNC__
Definition aurostd.h:43
#define _INDEX_ILLEGAL_
#define _FILE_WRONG_FORMAT_
#define _VALUE_ILLEGAL_
std::pair< size_t, size_t > find_bracket(const std::string &raw_content, char kind_open, std::pair< size_t, size_t > border={0, 0})
find the border of an encapsulated by brackets
std::pair< size_t, size_t > find_string(const std::string &raw_content, std::pair< size_t, size_t > border={0, 0})
find the border of a JSON string
std::string toString(const object &root, bool escape_unicode=false)
convert JSON::object to string
void saveFile(const object &root, const std::string &file_path, compression_type ct=compression_type::None, bool escape_unicode=true)
save JSON::object to file
std::string parse_string(const std::string &raw_content, std::pair< size_t, size_t > border={0, 0})
parse JSON string
std::map< std::string, object > Dictionary
shortcut for JSON::object_types::DICTIONARY
object parse(const std::string &raw_content, std::pair< size_t, size_t > border={0, 0})
parse a raw JSON string
std::string unescape_unicode(const std::string &raw, size_t &pos)
unescape JSON unicode instances
std::vector< object > List
shortcut for JSON::object_types::LIST
std::string char32_to_string(char32_t cp)
convert a unicode codepoint to a series of utf8 chars
object loadString(const std::string &content)
create a JSON::object from raw string
std::pair< size_t, size_t > find_strip(const std::string &raw_content, std::pair< size_t, size_t > border={0, 0})
strip whitespaces
object loadFile(const std::string &file_path)
create a JSON::object from file
std::string escape(const std::string &raw, bool unicode=true)
prepare string for JSON output with or without Unicode escapes
std::string char_escape(char16_t c)
escape characters to JSON
bool string2file(const string &StringOUTPUT, const std::string &FileNameRawOUTPUT, const compression_type ct, const string &mode)
write string into a file
static size_t range_find(const char *content_ptr, const std::pair< size_t, size_t > &border, const char to_find)
find a char inbetween a boundary
size_t file2string(const string &FileNameIN, string &StringIN)
read the content of a file into a string
utype string2utype(const string &from, const uint base=10)
utype min(const vector< utype > vec)
b64_encoder_proxy operator<<(std::ostream &os, b64_encoder_creator)
storge container for a JSON object
size_t count(const std::string &) const
count the occurrence of key string in a JSON::object_type::DICTIONARY
JSON::object & operator[](size_t index) const
direct index access to JSON::object_types::LIST objects
void fromDictionary(const Dictionary &content)
change this JSON::object to a JSON::object_types::DICTIONARY
std::vector< std::string > keys() const
returns a vector of all keys in a JSON::object_type::DICTIONARY
size_t size() const
gives the size of JSON::object_tpe::LIST, JSON::object_tpe::DICTIONARY or JSON::object_tpe::STRING
JSON::object & operator=(const char *content)
assignment operator for char
void fromList(const List &content)
change this JSON::object to a JSON::object_types::LIST
std::map< std::string, JSON::object >::iterator begin() const
returns an iterator to the begin() of a JSON::object_type::DICTIONARY
std::map< std::string, JSON::object >::iterator find(const std::string &key) const
returns an iterator to the result of find in JSON::object_type::DICTIONARY
void push_back(const JSON::object &content) const
allow to append to JSON::object_type::LIST
std::shared_ptr< void > obj
std::string toString(bool json_format=true, bool escape_unicode=true) const
converts a JSON::object into a string
std::map< std::string, JSON::object >::iterator end() const
returns an iterator to the end() of a JSON::object_type::DICTIONARY
void fromString(const std::string &content)
change this JSON::object to a JSON::object_types::STRING
bool empty() const
checks if JSON::object_tpe::LIST, JSON::object_tpe::DICTIONARY or JSON::object_tpe::STRING is empty
void join(const JSON::object &content) const
allow to merge two dictionaries or lists
void saveFile(const std::string &file_path, compression_type ct=compression_type::None, bool escape_unicode=true) const
save JSON::object to file