// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Andrey N. Sabelnikov nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include #include #include #include "parserse_base_utils.h" #include "file_io_utils.h" #define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100 namespace epee { using namespace misc_utils::parse; namespace serialization { namespace json { #define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));} /*inline void parse_error() { ASSERT_MES_AND_THROW("json parse error"); }*/ template inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion) { CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded"); std::string name; typename t_storage::harray h_array = nullptr; enum match_state { match_state_lookup_for_section_start, match_state_lookup_for_name, match_state_waiting_separator, match_state_wonder_after_separator, match_state_wonder_after_value, match_state_wonder_array, match_state_array_after_value, match_state_array_waiting_value, match_state_error }; enum array_mode { array_mode_undifined = 0, array_mode_sections, array_mode_string, array_mode_numbers, array_mode_booleans }; match_state state = match_state_lookup_for_section_start; array_mode array_md = array_mode_undifined; std::string::const_iterator it = sec_buf_begin; for(;it != buf_end;it++) { switch (state) { case match_state_lookup_for_section_start: if(*it == '{') state = match_state_lookup_for_name; else CHECK_ISSPACE(); break; case match_state_lookup_for_name: switch(*it) { case '"': match_string2(it, buf_end, name); state = match_state_waiting_separator; break; case '}': //this is it! section ends here. //seems that it is empty section sec_buf_begin = it; return; default: CHECK_ISSPACE(); } break; case match_state_waiting_separator: if(*it == ':') state = match_state_wonder_after_separator; else CHECK_ISSPACE(); break; case match_state_wonder_after_separator: if(*it == '"') {//just a named string value started std::string val; match_string2(it, buf_end, val); //insert text value stg.set_value(name, std::move(val), current_section); state = match_state_wonder_after_value; }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-') {//just a named number value started boost::string_ref val; bool is_v_float = false;bool is_signed = false; match_number2(it, buf_end, val, is_v_float, is_signed); if(!is_v_float) { if(is_signed) { errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); stg.set_value(name, int64_t(nval), current_section); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); stg.set_value(name, uint64_t(nval), current_section); } }else { errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); stg.set_value(name, double(nval), current_section); } state = match_state_wonder_after_value; }else if(isalpha(*it) ) {// could be null, true or false boost::string_ref word; match_word2(it, buf_end, word); if(boost::iequals(word, "null")) { state = match_state_wonder_after_value; //just skip this, }else if(boost::iequals(word, "true")) { stg.set_value(name, true, current_section); state = match_state_wonder_after_value; }else if(boost::iequals(word, "false")) { stg.set_value(name, false, current_section); state = match_state_wonder_after_value; }else ASSERT_MES_AND_THROW("Unknown value keyword " << word); }else if(*it == '{') { //sub section here typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_wonder_after_value; }else if(*it == '[') {//array of something state = match_state_wonder_array; }else CHECK_ISSPACE(); break; case match_state_wonder_after_value: if(*it == ',') state = match_state_lookup_for_name; else if(*it == '}') { //this is it! section ends here. sec_buf_begin = it; return; }else CHECK_ISSPACE(); break; case match_state_wonder_array: if(*it == '[') { ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry"); //mean array of array } if(*it == '{') { //mean array of sections typename t_storage::hsection new_sec = nullptr; h_array = stg.insert_first_section(name, new_sec, current_section); CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; array_md = array_mode_sections; }else if(*it == '"') { //mean array of strings std::string val; match_string2(it, buf_end, val); h_array = stg.insert_first_value(name, std::move(val), current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry"); state = match_state_array_after_value; array_md = array_mode_string; }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-') {//array of numbers value started boost::string_ref val; bool is_v_float = false;bool is_signed_val = false; match_number2(it, buf_end, val, is_v_float, is_signed_val); if(!is_v_float) { if (is_signed_val) { errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); h_array = stg.insert_first_value(name, int64_t(nval), current_section); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); h_array = stg.insert_first_value(name, uint64_t(nval), current_section); } CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); }else { errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); h_array = stg.insert_first_value(name, double(nval), current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); } state = match_state_array_after_value; array_md = array_mode_numbers; }else if(*it == ']')//empty array { array_md = array_mode_undifined; state = match_state_wonder_after_value; }else if(isalpha(*it) ) {// array of booleans boost::string_ref word; match_word2(it, buf_end, word); if(boost::iequals(word, "true")) { h_array = stg.insert_first_value(name, true, current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); state = match_state_array_after_value; array_md = array_mode_booleans; }else if(boost::iequals(word, "false")) { h_array = stg.insert_first_value(name, false, current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); state = match_state_array_after_value; array_md = array_mode_booleans; }else ASSERT_MES_AND_THROW("Unknown value keyword " << word) }else CHECK_ISSPACE(); break; case match_state_array_after_value: if(*it == ',') state = match_state_array_waiting_value; else if(*it == ']') { h_array = nullptr; array_md = array_mode_undifined; state = match_state_wonder_after_value; }else CHECK_ISSPACE(); break; case match_state_array_waiting_value: switch(array_md) { case array_mode_sections: if(*it == '{') { typename t_storage::hsection new_sec = NULL; bool res = stg.insert_next_section(h_array, new_sec); CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; }else CHECK_ISSPACE(); break; case array_mode_string: if(*it == '"') { std::string val; match_string2(it, buf_end, val); bool res = stg.insert_next_value(h_array, std::move(val)); CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values"); state = match_state_array_after_value; }else CHECK_ISSPACE(); break; case array_mode_numbers: if (epee::misc_utils::parse::isdigit(*it) || *it == '-') {//array of numbers value started boost::string_ref val; bool is_v_float = false;bool is_signed_val = false; match_number2(it, buf_end, val, is_v_float, is_signed_val); bool insert_res = false; if(!is_v_float) { if (is_signed_val) { errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); insert_res = stg.insert_next_value(h_array, int64_t(nval)); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); insert_res = stg.insert_next_value(h_array, uint64_t(nval)); } }else { errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); insert_res = stg.insert_next_value(h_array, double(nval)); } CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); state = match_state_array_after_value; array_md = array_mode_numbers; }else CHECK_ISSPACE(); break; case array_mode_booleans: if(isalpha(*it) ) {// array of booleans boost::string_ref word; match_word2(it, buf_end, word); if(boost::iequals(word, "true")) { bool r = stg.insert_next_value(h_array, true); CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); state = match_state_array_after_value; }else if(boost::iequals(word, "false")) { bool r = stg.insert_next_value(h_array, false); CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); state = match_state_array_after_value; } else ASSERT_MES_AND_THROW("Unknown value keyword " << word); }else CHECK_ISSPACE(); break; case array_mode_undifined: default: ASSERT_MES_AND_THROW("Bad array state"); } break; case match_state_error: default: ASSERT_MES_AND_THROW("WRONG JSON STATE"); } } } /* { "firstName": "John", "lastName": "Smith", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": -10021, "have_boobs": true, "have_balls": false }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ], "phoneNumbers": [ "812 123-1234", "916 123-4567" ] } */ template inline bool load_from_json(const std::string& buff_json, t_storage& stg) { std::string::const_iterator sec_buf_begin = buff_json.begin(); try { run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0); return true; } catch(const std::exception& ex) { MERROR("Failed to parse json, what: " << ex.what()); return false; } catch(...) { MERROR("Failed to parse json"); return false; } } } } }