////////////////////////////////////////////////////////////////////////////////
// //
// Copyright (C) 2016, goatpig. //
// Distributed under the MIT license //
// See LICENSE-MIT or https://opensource.org/licenses/MIT //
// //
////////////////////////////////////////////////////////////////////////////////
#include "JSON_codec.h"
int JSON_object::id_counter_ = 0;
////////////////////////////////////////////////////////////////////////////////
JSON_value::~JSON_value()
{}
////////////////////////////////////////////////////////////////////////////////
string JSON_encode(JSON_object& json_obj)
{
//make sure json_obj has jsonrpc, params and id key
auto rpciter = json_obj.keyval_pairs_.find(string("jsonrpc"));
if (rpciter == json_obj.keyval_pairs_.end())
json_obj.add_pair("jsonrpc", "2.0");
auto paramsiter = json_obj.keyval_pairs_.find(string("params"));
if (paramsiter == json_obj.keyval_pairs_.end())
{
JSON_array arr;
json_obj.add_pair("params", arr);
}
auto iditer = json_obj.keyval_pairs_.find(string("id"));
if (iditer == json_obj.keyval_pairs_.end())
json_obj.add_pair("id", json_obj.id_);
stringstream ss;
json_obj.serialize(ss);
return ss.str();
}
////////////////////////////////////////////////////////////////////////////////
void JSON_object::serialize(ostream& s) const
{
s << "{";
if (keyval_pairs_.size() > 0)
{
auto iter = keyval_pairs_.begin();
while (1)
{
iter->first.serialize(s);
s << ": ";
iter->second->serialize(s);
++iter;
if (iter == keyval_pairs_.end())
break;
s << ", ";
}
}
s << "}";
}
////////////////////////////////////////////////////////////////////////////////
void JSON_object::unserialize(istream& s)
{
keyval_pairs_.clear();
auto val = s.get();
if (val != '{')
throw JSON_Exception("invalid object encapsulation");
auto addPair = [this](JSON_string& key, shared_ptr val)->void
{
auto&& keyval = make_pair(move(key), val);
keyval_pairs_.insert(move(keyval));
};
vector value_vector;
while (s.good())
{
auto c = s.peek();
switch (c)
{
case ' ':
case ':':
case ',':
{
s.get();
continue;
}
case '\"':
{
auto json_string = make_shared();
json_string->unserialize(s);
if (value_vector.size() == 0)
{
value_vector.push_back(json_string->val_);
break;
}
auto key = value_vector.back();
addPair(key, json_string);
value_vector.pop_back();
break;
}
case '[':
{
if (value_vector.size() == 0)
throw JSON_Exception("missing object key");
auto json_array = make_shared();
json_array->unserialize(s);
auto key = value_vector.back();
addPair(key, json_array);
value_vector.pop_back();
break;
}
case '{':
{
if (value_vector.size() == 0)
throw JSON_Exception("missing object key");
auto json_object = make_shared();
json_object->unserialize(s);
auto key = value_vector.back();
addPair(key, json_object);
value_vector.pop_back();
break;
}
case '}':
{
s.get();
return;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
{
if (value_vector.size() == 0)
throw JSON_Exception("missing object key");
auto json_number = make_shared();
json_number->unserialize(s);
auto key = value_vector.back();
addPair(key, json_number);
value_vector.pop_back();
break;
}
case 't':
case 'n':
case 'f':
{
if (value_vector.size() == 0)
throw JSON_Exception("missing object key");
auto json_state = make_shared();
json_state->unserialize(s);
auto key = value_vector.back();
addPair(key, json_state);
value_vector.pop_back();
break;
}
default:
throw JSON_Exception("unexpected encapsulation");
}
}
}
////////////////////////////////////////////////////////////////////////////////
void JSON_string::unserialize(istream& s)
{
val_.clear();
auto val = s.get();
if (val != '\"')
throw JSON_Exception("invalid string encapsulation");
while (1)
{
string str;
getline(s, str, '\"');
if (s.rdstate() != ios_base::goodbit)
throw JSON_Exception("invalid string encapsulation");
val_.append(str);
//make sure that delimiting '\"' was no exited
auto len = str.size();
if (str.c_str()[len - 1] != '\\')
break;
val_.append("\"");
}
}
////////////////////////////////////////////////////////////////////////////////
void JSON_array::unserialize(istream& s)
{
values_.clear();
auto val = s.get();
if (val != '[')
throw JSON_Exception("invalid string encapsulation");
while (s.rdstate() == ios_base::goodbit)
{
auto c = s.peek();
switch (c)
{
case ' ':
case ',':
{
s.get();
continue;
}
case '\"':
{
auto json_string = make_shared();
json_string->unserialize(s);
values_.push_back(json_string);
break;
}
case '[':
{
auto json_array = make_shared();
json_array->unserialize(s);
values_.push_back(json_array);
break;
}
case ']':
{
s.get();
return;
}
case '{':
{
auto json_object = make_shared();
json_object->unserialize(s);
values_.push_back(json_object);
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
{
auto json_number = make_shared();
json_number->unserialize(s);
values_.push_back(json_number);
break;
}
default:
throw JSON_Exception("unexpected encapsulation");
}
}
}
////////////////////////////////////////////////////////////////////////////////
void JSON_state::unserialize(istream& s)
{
auto c = s.peek();
switch (c)
{
case 'n':
{
string val_null;
val_null.resize(4);
s.read(&val_null[0], 5);
if (val_null != "null")
throw JSON_Exception("invalid state");
state_ = JSON_null;
break;
}
case 't':
{
string val_true;
val_true.resize(4);
s.getline(&val_true[0], 5);
if (val_true != "true")
throw JSON_Exception("invalid state");
state_ = JSON_true;
break;
}
case 'f':
{
string val_false;
val_false.resize(5);
s.getline(&val_false[0], 6);
if (val_false != "false")
throw JSON_Exception("invalid state");
state_ = JSON_false;
break;
}
default:
throw JSON_Exception("unexpected state at deser");
}
if (s.fail())
s.clear();
}
////////////////////////////////////////////////////////////////////////////////
JSON_object JSON_decode(const string& json_str)
{
JSON_object obj;
stringstream ss(json_str);
obj.unserialize(ss);
return obj;
}
////////////////////////////////////////////////////////////////////////////////
shared_ptr JSON_object::getValForKey(const string& key)
{
auto&& keyStr = JSON_string(string(key));
auto pairIter = keyval_pairs_.find(keyStr);
if (pairIter == keyval_pairs_.end())
return nullptr;
return pairIter->second;
}
////////////////////////////////////////////////////////////////////////////////
bool JSON_object::isResponseValid(int id)
{
//check id
auto idVal = getValForKey("id");
auto id_obj = dynamic_pointer_cast(idVal);
if (id_obj == nullptr)
return false;
if (int(id_obj->val_) != id)
return false;
//check "error": null
auto errorVal = getValForKey("error");
auto error_obj = dynamic_pointer_cast(errorVal);
if (error_obj == nullptr)
return false;
if (error_obj->state_ != JSON_null)
return false;
return true;
}