2014-07-25 10:29:08 -06:00
|
|
|
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
2014-03-03 15:07:58 -07:00
|
|
|
// All rights reserved.
|
|
|
|
//
|
2014-07-25 10:29:08 -06:00
|
|
|
// 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.
|
2014-03-03 15:07:58 -07:00
|
|
|
//
|
2014-07-25 10:29:08 -06:00
|
|
|
// 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.
|
2014-03-03 15:07:58 -07:00
|
|
|
//
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
#include "parserse_base_utils.h"
|
|
|
|
#include "file_io_utils.h"
|
|
|
|
|
|
|
|
namespace epee
|
|
|
|
{
|
|
|
|
using namespace misc_utils::parse;
|
|
|
|
namespace serialization
|
|
|
|
{
|
|
|
|
namespace json
|
|
|
|
{
|
|
|
|
#define CHECK_ISSPACE() if(!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<class t_storage>
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
|
|
|
|
std::string::const_iterator sub_element_start;
|
|
|
|
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, val, current_section);
|
|
|
|
state = match_state_wonder_after_value;
|
|
|
|
}else if (isdigit(*it) || *it == '-')
|
|
|
|
{//just a named number value started
|
|
|
|
std::string 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)
|
|
|
|
{
|
|
|
|
int64_t nval = boost::lexical_cast<int64_t>(val);
|
|
|
|
stg.set_value(name, nval, current_section);
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
uint64_t nval = boost::lexical_cast<uint64_t >(val);
|
|
|
|
stg.set_value(name, nval, current_section);
|
|
|
|
}
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
double nval = boost::lexical_cast<double>(val);
|
|
|
|
stg.set_value(name, nval, current_section);
|
|
|
|
}
|
|
|
|
state = match_state_wonder_after_value;
|
|
|
|
}else if(isalpha(*it) )
|
|
|
|
{// could be null, true or false
|
|
|
|
std::string 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);
|
|
|
|
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);
|
|
|
|
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, 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 (isdigit(*it) || *it == '-')
|
|
|
|
{//array of numbers value started
|
|
|
|
std::string 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)
|
|
|
|
{
|
|
|
|
int64_t nval = boost::lexical_cast<int64_t>(val);//bool res = string_tools::string_to_num_fast(val, nval);
|
|
|
|
h_array = stg.insert_first_value(name, nval, current_section);
|
|
|
|
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
double nval = boost::lexical_cast<double>(val);//bool res = string_tools::string_to_num_fast(val, nval);
|
|
|
|
h_array = stg.insert_first_value(name, 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
|
|
|
|
std::string 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);
|
|
|
|
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, 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 (isdigit(*it) || *it == '-')
|
|
|
|
{//array of numbers value started
|
|
|
|
std::string 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)
|
|
|
|
{
|
2014-03-20 05:46:11 -06:00
|
|
|
int64_t nval = boost::lexical_cast<int64_t>(val); //bool res = string_tools::string_to_num_fast(val, nval);
|
2014-03-03 15:07:58 -07:00
|
|
|
insert_res = stg.insert_next_value(h_array, nval);
|
|
|
|
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
//TODO: optimize here if need
|
|
|
|
double nval = boost::lexical_cast<double>(val); //string_tools::string_to_num_fast(val, nval);
|
|
|
|
insert_res = stg.insert_next_value(h_array, 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
|
|
|
|
std::string 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<class t_storage>
|
|
|
|
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);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch(const std::exception& ex)
|
|
|
|
{
|
|
|
|
LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
LOG_PRINT_RED_L0("Failed to parse json");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|