864 lines
22 KiB
C++
864 lines
22 KiB
C++
// Copyright (c) 2021 Robin Davies
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
// this software and associated documentation files (the "Software"), to deal in
|
|
// the Software without restriction, including without limitation the rights to
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
|
// subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
#pragma once
|
|
#include <iostream>
|
|
#include <cstdint>
|
|
#include <boost/utility/string_view.hpp>
|
|
#include <boost/format.hpp>
|
|
#include <iomanip>
|
|
#include <type_traits>
|
|
#include <sstream>
|
|
#include "HtmlHelper.hpp"
|
|
#include <cctype>
|
|
#include "PiPedalException.hpp"
|
|
|
|
|
|
|
|
#define DECLARE_JSON_MAP(CLASSNAME) \
|
|
static json_map::storage_type<CLASSNAME> jmap
|
|
|
|
#define JSON_MAP_BEGIN(CLASSNAME) \
|
|
json_map::storage_type<CLASSNAME> CLASSNAME::jmap { {
|
|
|
|
#define JSON_MAP_REFERENCE(class,name) \
|
|
json_map::reference(#name,&class::name##_),
|
|
|
|
#define JSON_MAP_REFERENCE_CONDITIONAL(class,name, conditionFn) \
|
|
json_map::conditional_reference(#name,&class::name##_,conditionFn) ,
|
|
|
|
|
|
#define JSON_MAP_END() \
|
|
}};
|
|
|
|
namespace pipedal
|
|
{
|
|
|
|
// a function pointer of type bool (*)(const CLASS*,const MEMBER_TYPE&value) (because pre-C++ 20 templated function pointers are *hard*)
|
|
template<typename CLASS,typename MEMBER_TYPE> class JsonConditionFunction {
|
|
public:
|
|
using Pointer = bool (*)(const CLASS*self,const MEMBER_TYPE&value);
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
|
class json_reader;
|
|
class json_writer;
|
|
|
|
class JsonWritable {
|
|
public:
|
|
virtual void write_members(json_writer&writer) const = 0;
|
|
};
|
|
|
|
template<typename CLASS>
|
|
class json_member_reference_base {
|
|
private:
|
|
const char*name_;
|
|
protected:
|
|
json_member_reference_base(const char*name)
|
|
:name_(name)
|
|
{
|
|
}
|
|
public:
|
|
virtual ~json_member_reference_base() {}
|
|
const char*name() { return this->name_; }
|
|
virtual bool canWrite(const CLASS *self) { return true; }
|
|
virtual void read_value(json_reader&reader, CLASS*self) = 0;
|
|
virtual void write_value(json_writer&writer, const CLASS*self) = 0;
|
|
};
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
class json_member_reference: public json_member_reference_base<CLASS> {
|
|
public:
|
|
virtual ~json_member_reference() {}
|
|
json_member_reference(const char*name, MEMBER_TYPE CLASS::*member_pointer)
|
|
: json_member_reference_base<CLASS>(name)
|
|
, member_pointer(member_pointer)
|
|
{
|
|
}
|
|
MEMBER_TYPE CLASS::*member_pointer;
|
|
|
|
void read_value(json_reader&reader, CLASS*self);
|
|
void write_value(json_writer&writer, const CLASS*self);
|
|
|
|
|
|
};
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
class json_conditional_member_reference: public json_member_reference_base<CLASS> {
|
|
public:
|
|
virtual ~json_conditional_member_reference() {}
|
|
|
|
json_conditional_member_reference(const char*name, MEMBER_TYPE CLASS::*member_pointer,typename JsonConditionFunction<CLASS,MEMBER_TYPE>::Pointer condition)
|
|
: json_member_reference_base<CLASS>(name)
|
|
, member_pointer(member_pointer)
|
|
, condition_(condition)
|
|
{
|
|
}
|
|
MEMBER_TYPE CLASS::*member_pointer;
|
|
|
|
typename JsonConditionFunction<CLASS,MEMBER_TYPE>::Pointer condition_;
|
|
|
|
virtual bool canWrite(const CLASS *self);
|
|
|
|
void read_value(json_reader&reader, CLASS*self);
|
|
void write_value(json_writer&writer, const CLASS*self);
|
|
};
|
|
|
|
|
|
|
|
template <typename CLASS>
|
|
class json_enum_converter {
|
|
public:
|
|
virtual CLASS fromString(const std::string&value) const = 0;
|
|
virtual const std::string& toString(CLASS value) const = 0;
|
|
};
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
class json_enum_member_reference: public json_member_reference_base<CLASS> {
|
|
public:
|
|
json_enum_member_reference(const char*name, MEMBER_TYPE CLASS::*member_pointer, const json_enum_converter<MEMBER_TYPE>*converter)
|
|
: json_member_reference_base<CLASS>(name)
|
|
, member_pointer(member_pointer)
|
|
, converter_(converter)
|
|
{
|
|
}
|
|
MEMBER_TYPE CLASS::*member_pointer;
|
|
const json_enum_converter<MEMBER_TYPE> *converter_;
|
|
|
|
|
|
void read_value(json_reader&reader, CLASS*self);
|
|
void write_value(json_writer&writer, const CLASS*self);
|
|
|
|
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
class json_map_base {
|
|
|
|
};
|
|
|
|
template<typename CLASS>
|
|
class json_map_impl: public json_map_base {
|
|
private:
|
|
std::vector<json_member_reference_base<CLASS>* > members_;
|
|
json_map_impl(const json_map_impl&) { } // disable copy constructor.
|
|
public:
|
|
json_map_impl(std::vector<json_member_reference_base<CLASS>*> members)
|
|
: members_(members)
|
|
{
|
|
|
|
}
|
|
virtual ~json_map_impl()
|
|
{
|
|
for (auto member: members_)
|
|
{
|
|
delete member;
|
|
}
|
|
members_.resize(0);
|
|
}
|
|
void read_property(json_reader* reader,const char*memberName,CLASS *pObject);
|
|
void write_members(json_writer* writer, const CLASS*pObject);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class json_map {
|
|
public:
|
|
template <typename CLASS> class storage_type : public json_map_impl<CLASS> {
|
|
public:
|
|
storage_type(std::vector<json_member_reference_base<CLASS>*> members)
|
|
: json_map_impl<CLASS>(members)
|
|
{
|
|
}
|
|
};
|
|
template <typename CLASS, typename MEMBER_TYPE>
|
|
static json_member_reference<CLASS,MEMBER_TYPE> *
|
|
reference(const char*name,MEMBER_TYPE CLASS::*member_pointer)
|
|
{
|
|
return new json_member_reference<CLASS,MEMBER_TYPE>(name,member_pointer);
|
|
};
|
|
template <typename CLASS, typename MEMBER_TYPE,typename ENUM_CONVERTER>
|
|
static json_enum_member_reference<CLASS,MEMBER_TYPE> *
|
|
enum_reference(
|
|
const char*name,
|
|
MEMBER_TYPE CLASS::*member_pointer,
|
|
const ENUM_CONVERTER*converter)
|
|
{
|
|
return new json_enum_member_reference<CLASS,MEMBER_TYPE>(name,member_pointer,converter);
|
|
};
|
|
|
|
template <typename CLASS, typename MEMBER_TYPE>
|
|
static json_conditional_member_reference<CLASS,MEMBER_TYPE> *
|
|
conditional_reference(const char*name,MEMBER_TYPE CLASS::*member_pointer,typename JsonConditionFunction<CLASS,MEMBER_TYPE>::Pointer condition )
|
|
{
|
|
return new json_conditional_member_reference<CLASS,MEMBER_TYPE>(name,member_pointer,condition);
|
|
};
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
class HasJsonPropertyMap {
|
|
|
|
template <class TYPE>
|
|
static std::true_type test (decltype(TYPE::jmap) *) { return std::true_type();};
|
|
|
|
template <class TYPE>
|
|
static std::false_type test (...);
|
|
|
|
public:
|
|
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class json_writer;
|
|
|
|
|
|
class json_writer {
|
|
public:
|
|
const char*CRLF;
|
|
private:
|
|
std::ostream &os;
|
|
int indent_level;
|
|
bool compressed;
|
|
const int TAB_SIZE = 2;
|
|
|
|
const uint16_t UTF16_SURROGATE_1_BASE = 0xD800U;
|
|
const uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U;
|
|
const uint16_t UTF16_SURROGATE_MASK = 0x3FFFU;
|
|
|
|
static const uint32_t UTF8_ONE_BYTE_MASK = 0x80;
|
|
static const uint32_t UTF8_ONE_BYTE_BITS = 0;
|
|
static const uint32_t UTF8_TWO_BYTES_MASK = 0xE0;
|
|
static const uint32_t UTF8_TWO_BYTES_BITS = 0xC0;
|
|
static const uint32_t UTF8_THREE_BYTES_MASK = 0xF0;
|
|
static const uint32_t UTF8_THREE_BYTES_BITS = 0xE0;
|
|
static const uint32_t UTF8_FOUR_BYTES_MASK = 0xF8;
|
|
static const uint32_t UTF8_FOUR_BYTES_BITS = 0xF0;
|
|
static const uint32_t UTF8_CONTINUATION_MASK = 0xC0;
|
|
static const uint32_t UTF8_CONTINUATION_BITS = 0x80;
|
|
|
|
|
|
public:
|
|
static std::string encode_string(const std::string &text)
|
|
{
|
|
std::stringstream s;
|
|
json_writer writer(s);
|
|
writer.write(text);
|
|
return s.str();
|
|
}
|
|
void write_raw(const char*text) {
|
|
os << text;
|
|
}
|
|
using string_view = boost::string_view;
|
|
json_writer(std::ostream &os, bool compressed = false)
|
|
: os(os)
|
|
, compressed(compressed)
|
|
, indent_level(0)
|
|
{
|
|
this->CRLF = compressed? "" : "\r\n";
|
|
}
|
|
void write(long long value)
|
|
{
|
|
os << value;
|
|
}
|
|
void write(unsigned long long value)
|
|
{
|
|
os << value;
|
|
}
|
|
void write(long value)
|
|
{
|
|
os << value;
|
|
}
|
|
void write(unsigned long value)
|
|
{
|
|
os << value;
|
|
}
|
|
void write(int value)
|
|
{
|
|
os << value;
|
|
}
|
|
void write(unsigned int value)
|
|
{
|
|
os << value;
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
static void throw_encoding_error();
|
|
|
|
static uint32_t continuation_byte(std::string_view::iterator &p, std::string_view::const_iterator end);
|
|
void write_utf16_char(uint16_t uc);
|
|
|
|
public:
|
|
void indent();
|
|
std::ostream & output_stream() { return os; }
|
|
|
|
void write(bool value) {
|
|
os << (value? "true": "false");
|
|
}
|
|
void write(
|
|
string_view v,
|
|
bool enforceValidUtf8 // throw errors for illegal (non-minimal) UTF-8 sequences.
|
|
);
|
|
void write(string_view v)
|
|
{
|
|
write(v,true);
|
|
}
|
|
void write(const char*p)
|
|
{
|
|
write(string_view(p));
|
|
}
|
|
|
|
void write(const std::string&s)
|
|
{
|
|
write(string_view(s.c_str()));
|
|
}
|
|
void write(float f)
|
|
{
|
|
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
|
}
|
|
void write(double f)
|
|
{
|
|
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
|
}
|
|
|
|
template <typename T> void write(const std::vector<T> &value)
|
|
{
|
|
if (std::is_fundamental<T>() || std::is_assignable<T,const char*>() || value.size() == 0)
|
|
{
|
|
// simple types: all on same line.
|
|
os << "[ ";
|
|
|
|
if (value.size() >= 1)
|
|
{
|
|
write(value[0]);
|
|
}
|
|
for (size_t i = 1; i < value.size(); ++i)
|
|
{
|
|
os << ",";
|
|
write(value[i]);
|
|
}
|
|
os << "]";
|
|
} else {
|
|
// complex types: one line per entry.
|
|
os << "[" << CRLF;
|
|
indent_level += TAB_SIZE;
|
|
bool first = true;
|
|
for (size_t i = 0; i < value.size(); ++i)
|
|
{
|
|
if (!first)
|
|
{
|
|
os << ',' << CRLF;
|
|
}
|
|
first = false;
|
|
indent();
|
|
write(value[i]);
|
|
}
|
|
indent_level -= TAB_SIZE;
|
|
os << CRLF;
|
|
indent();
|
|
os << "]";
|
|
|
|
}
|
|
}
|
|
template<
|
|
class Category,
|
|
class T,
|
|
class Distance = std::ptrdiff_t,
|
|
class Pointer = T*,
|
|
class Reference = T&
|
|
> void write(std::iterator<Category,T,Distance,Pointer,Reference> &it) {
|
|
start_array();
|
|
bool first = true;
|
|
for( Reference ref: it)
|
|
{
|
|
if (!first)
|
|
{
|
|
os << ", ";
|
|
os << CRLF;
|
|
}
|
|
indent();
|
|
os << ref;
|
|
}
|
|
end_array();
|
|
}
|
|
|
|
void write_json_member(const char*name, const char*json_text)
|
|
{
|
|
write(name);
|
|
os << ": ";
|
|
os << json_text;
|
|
}
|
|
|
|
template<typename T> void write_member(const char*name,const T&value)
|
|
{
|
|
write(name);
|
|
os << ": ";
|
|
write(value);
|
|
}
|
|
void start_object();
|
|
void end_object();
|
|
void start_array();
|
|
void end_array();
|
|
|
|
|
|
/*
|
|
void write(const json_writable *obj)
|
|
{
|
|
if (obj == nullptr)
|
|
{
|
|
os << "null";
|
|
} else {
|
|
start_object();
|
|
obj->write(*this);
|
|
end_object();
|
|
}
|
|
}
|
|
void write(const json_writable &obj)
|
|
{
|
|
start_object();
|
|
obj.write(*this);
|
|
end_object();
|
|
}
|
|
*/
|
|
private:
|
|
template<typename T>
|
|
json_map::storage_type<T>& get_object_map()
|
|
{
|
|
//static_assert(HasJsonPropertyMap<T>::value,"Type does not have a Json propery map name jmap.");
|
|
// Type T must have public static propery jmap.
|
|
return T::jmap;
|
|
}
|
|
public:
|
|
|
|
void write(const JsonWritable*pWritable)
|
|
{
|
|
start_object();
|
|
pWritable->write_members(*this);
|
|
end_object();
|
|
}
|
|
template <typename T>
|
|
void write(const std::shared_ptr<T> &obj)
|
|
{
|
|
write(obj.get());
|
|
}
|
|
template <typename T>
|
|
void write(const std::weak_ptr<T> &obj)
|
|
{
|
|
auto p = obj.lock();
|
|
write(p.get());
|
|
}
|
|
template <typename T>
|
|
void write(const std::unique_ptr<T> &obj)
|
|
{
|
|
write(obj.get());
|
|
}
|
|
|
|
template <typename T>
|
|
void write(const T*obj)
|
|
{
|
|
static_assert(HasJsonPropertyMap<T>::value,"Json serialization type not supported. (Json object classes must have a Json property map named jmap).");
|
|
if (obj == nullptr)
|
|
{
|
|
write_raw("null");
|
|
} else {
|
|
write(*obj);
|
|
}
|
|
|
|
}
|
|
template <typename T>
|
|
void write(T*obj)
|
|
{
|
|
static_assert(HasJsonPropertyMap<T>::value,"Json serialization type not supported. (Json object classes must have a Json property map named jmap).");
|
|
if (obj == nullptr)
|
|
{
|
|
write_raw("null");
|
|
} else {
|
|
write(*obj);
|
|
}
|
|
|
|
}
|
|
|
|
template <typename T>
|
|
void write(const T&obj)
|
|
{
|
|
static_assert(HasJsonPropertyMap<T>::value,"Json serialization type not supported. (Json object classes must have a Json property map named jmap).");
|
|
|
|
auto &map = get_object_map<T>();
|
|
start_object();
|
|
|
|
map.write_members(this,&obj);
|
|
|
|
end_object();
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
class json_reader {
|
|
std::istream &is_;
|
|
|
|
const uint16_t UTF16_SURROGATE_1_BASE = 0xD800U;
|
|
const uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U;
|
|
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
|
|
|
|
|
public:
|
|
json_reader(std::istream &input)
|
|
: is_(input)
|
|
{
|
|
|
|
}
|
|
|
|
private:
|
|
void throw_format_error(const char*error);
|
|
|
|
void throw_format_error()
|
|
{
|
|
throw_format_error("Invalid file format");
|
|
}
|
|
|
|
|
|
|
|
inline bool is_whitespace(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 0x20:
|
|
case 0x0A:
|
|
case 0x0D:
|
|
case 0x09:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
char get() {
|
|
int ic = is_.get();
|
|
if (ic == -1) throw_format_error("Unexpected end of file");
|
|
return (char)ic;
|
|
}
|
|
|
|
void skip_whitespace();
|
|
|
|
void read_object_start();
|
|
uint16_t read_hex();
|
|
uint16_t read_u_escape();
|
|
|
|
template<typename T>
|
|
json_map::storage_type<T>& get_object_map()
|
|
{
|
|
// Type T must have public static propery map.
|
|
return T::jmap;
|
|
}
|
|
public:
|
|
void consume(char expected);
|
|
void consumeToken(const char*expectedToken, const char*errorMessage);
|
|
int peek()
|
|
{
|
|
skip_whitespace();
|
|
return is_.peek();
|
|
}
|
|
public:
|
|
std::string read_string();
|
|
|
|
public:
|
|
bool is_complete();
|
|
|
|
template<typename T>
|
|
void read(T*pObject)
|
|
{
|
|
char c;
|
|
consume('{');
|
|
|
|
auto &map = get_object_map<T>();
|
|
while (true)
|
|
{
|
|
c = peek();
|
|
if (c == '}')
|
|
{
|
|
c = get();
|
|
break;
|
|
}
|
|
std::string memberName = read_string();
|
|
|
|
consume(':');
|
|
skip_whitespace();
|
|
|
|
map.read_property(this,memberName.c_str(),pObject);
|
|
|
|
skip_whitespace();
|
|
if (is_.peek() == ',')
|
|
{
|
|
c = get();
|
|
}
|
|
}
|
|
}
|
|
template<typename T>
|
|
void read(T**pObject)
|
|
{
|
|
int c = peek();
|
|
if (c != '{')
|
|
{
|
|
if (c == 'n')
|
|
{
|
|
consumeToken("null", "Expecting '{' or 'null'.");
|
|
*pObject = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
*pObject = new T();
|
|
read(*pObject);
|
|
}
|
|
|
|
template<typename T>
|
|
void read(std::unique_ptr<T> *pUniquePtr)
|
|
{
|
|
std::unique_ptr<T> p = std::make_unique<T>();
|
|
|
|
read(p.get());
|
|
*pUniquePtr = std::move(p);
|
|
}
|
|
template<typename T>
|
|
void read(std::shared_ptr<T> *pUniquePtr)
|
|
{
|
|
std::shared_ptr<T> p = std::make_shared<T>();
|
|
|
|
read(p.get());
|
|
(*pUniquePtr) = std::move(p);
|
|
}
|
|
template<typename T>
|
|
void read(std::weak_ptr<T> *pUniquePtr)
|
|
{
|
|
throw std::domain_error("Can't read std::weak_ptr");
|
|
}
|
|
|
|
private:
|
|
void skip_string();
|
|
void skip_number();
|
|
void skip_array();
|
|
void skip_object();
|
|
std::string readToken();
|
|
bool read_boolean();
|
|
void read_null();
|
|
void skip_token();
|
|
public:
|
|
void skip_property();
|
|
|
|
void read(std::string*value) {
|
|
skip_whitespace();
|
|
*value = read_string();
|
|
}
|
|
|
|
void read(bool *value)
|
|
{
|
|
skip_whitespace();
|
|
*value = read_boolean();
|
|
}
|
|
void read(int *value) {
|
|
skip_whitespace();
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(long*value)
|
|
{
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(long long*value) {
|
|
skip_whitespace();
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(unsigned int*value)
|
|
{
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(unsigned long*value)
|
|
{
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(unsigned long long*value) {
|
|
skip_whitespace();
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
|
|
void read(float*value) {
|
|
skip_whitespace();
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
void read(double*value) {
|
|
skip_whitespace();
|
|
is_ >> *value;
|
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
|
}
|
|
template <typename T>
|
|
void read(std::vector<T>*value)
|
|
{
|
|
char c;
|
|
std::vector<T> result;
|
|
|
|
consume('[');
|
|
while (true)
|
|
{
|
|
if (peek() == ']')
|
|
{
|
|
c = get();
|
|
break;
|
|
}
|
|
T item;
|
|
read(&item);
|
|
result.push_back(std::move(item));
|
|
if (peek() == ',')
|
|
{
|
|
c = get();
|
|
}
|
|
}
|
|
*value = std::move(result);
|
|
}
|
|
|
|
|
|
};
|
|
|
|
template <typename T>
|
|
class HasJsonRead {
|
|
|
|
|
|
template <typename TYPE, typename ARG>
|
|
static std::true_type test (decltype(json_reader(std::cin).read((ARG*)nullptr))*v) { return std::true_type();};
|
|
|
|
template <typename TYPE, typename ARG>
|
|
static std::false_type test (...);
|
|
|
|
public:
|
|
static constexpr bool value = decltype(test<json_reader,T>(nullptr))::value;
|
|
};
|
|
|
|
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
|
static_assert(HasJsonRead<MEMBER_TYPE>::value,"Type not supported for Json serialization.");
|
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
|
reader.read(ref);
|
|
}
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
|
writer.write_member(this->name(),ref);
|
|
}
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_enum_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
|
std::string val = reader.read_string();
|
|
*ref = converter_->fromString(val);
|
|
}
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_enum_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
|
std::string val = converter_->toString(ref);
|
|
writer.write_member(this->name(),val);
|
|
}
|
|
|
|
|
|
template <typename CLASS>
|
|
void json_map_impl<CLASS>::read_property(json_reader* reader,const char*memberName,CLASS *pObject)
|
|
{
|
|
for (json_member_reference_base<CLASS>*member: members_)
|
|
{
|
|
if (strcmp(member->name(),memberName) == 0)
|
|
{
|
|
member->read_value(*reader,pObject);
|
|
return;
|
|
}
|
|
|
|
}
|
|
reader->skip_property();
|
|
}
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_conditional_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
|
static_assert(HasJsonRead<MEMBER_TYPE>::value,"Type not supported for Json serialization.");
|
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
|
reader.read(ref);
|
|
}
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
bool json_conditional_member_reference<CLASS,MEMBER_TYPE>::canWrite(const CLASS*self) {
|
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
|
return this->condition_(self,ref);
|
|
|
|
}
|
|
|
|
|
|
template<typename CLASS, typename MEMBER_TYPE>
|
|
void json_conditional_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
|
writer.write_member(this->name(),ref);
|
|
}
|
|
|
|
|
|
template <typename CLASS>
|
|
void json_map_impl<CLASS>::write_members(json_writer* writer, const CLASS*pObject)
|
|
{
|
|
bool first = true;
|
|
for (json_member_reference_base<CLASS>*member: members_)
|
|
{
|
|
if (member->canWrite(pObject))
|
|
{
|
|
if (!first)
|
|
{
|
|
writer->output_stream() << ',';
|
|
writer->output_stream() << writer->CRLF;
|
|
}
|
|
first = false;
|
|
writer->indent();
|
|
member->write_value(*writer,pObject);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace pipedal
|