Skip to content

Commit

Permalink
Merge pull request #33742 from jbytheway/more_std_serialization
Browse files Browse the repository at this point in the history
More easy serialization for STL containers
  • Loading branch information
ZhilkinSerg authored Sep 2, 2019
2 parents 8faae7a + 621b4c1 commit 33d3060
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 6 deletions.
80 changes: 74 additions & 6 deletions src/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class JsonArray;
class JsonSerializer;
class JsonDeserializer;

template<typename T>
class string_id;

class JsonError : public std::runtime_error
{
public:
Expand All @@ -45,6 +48,30 @@ class JsonError : public std::runtime_error
}
};

template<typename T, typename Enable = void>
struct key_from_json_string;

template<>
struct key_from_json_string<std::string, void> {
std::string operator()( const std::string &s ) {
return s;
}
};

template<typename T>
struct key_from_json_string<string_id<T>, void> {
string_id<T> operator()( const std::string &s ) {
return string_id<T>( s );
}
};

template<typename Enum>
struct key_from_json_string<Enum, std::enable_if_t<std::is_enum<Enum>::value>> {
Enum operator()( const std::string &s ) {
return io::string_to_enum<Enum>( s );
}
};

/* JsonIn
* ======
*
Expand Down Expand Up @@ -263,12 +290,35 @@ class JsonIn

template<typename T, std::enable_if_t<std::is_enum<T>::value, int> = 0>
bool read( T &val ) {
int i;
if( read( i ) ) {
val = static_cast<T>( i );
return true;
}
std::string s;
if( !read( s ) ) {
if( read( s ) ) {
val = io::string_to_enum<T>( s );
return true;
}
return false;
}

/// Overload for std::pair
template<typename T, typename U>
bool read( std::pair<T, U> &p ) {
if( !test_array() ) {
return false;
}
try {
start_array();
return !end_array() &&
read( p.first ) &&
!end_array() &&
read( p.second ) &&
end_array();
} catch( const JsonError & ) {
return false;
}
val = io::string_to_enum<T>( s );
return true;
}

// array ~> vector, deque, list
Expand Down Expand Up @@ -383,10 +433,11 @@ class JsonIn
start_object();
m.clear();
while( !end_object() ) {
typename T::key_type name( get_member_name() );
using key_type = typename T::key_type;
key_type key = key_from_json_string<key_type>()( get_member_name() );
typename T::mapped_type element;
if( read( element ) ) {
m[std::move( name )] = std::move( element );
m[std::move( key )] = std::move( element );
} else {
skip_value();
}
Expand Down Expand Up @@ -533,6 +584,23 @@ class JsonOut
write( io::enum_to_string<E>( value ) );
}

void write_as_string( const std::string &s ) {
write( s );
}

template<typename T>
void write_as_string( const string_id<T> &s ) {
write( s );
}

template<typename T, typename U>
void write( const std::pair<T, U> &p ) {
start_array();
write( p.first );
write( p.second );
end_array();
}

template <typename T>
void write_as_array( const T &container ) {
start_array();
Expand Down Expand Up @@ -574,7 +642,7 @@ class JsonOut
void write( const T &map ) {
start_object();
for( const auto &it : map ) {
write( it.first );
write_as_string( it.first );
write_member_separator();
write( it.second );
}
Expand Down
71 changes: 71 additions & 0 deletions tests/json_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "json.h"

#include <list>
#include <sstream>

#include "bodypart.h"
#include "catch/catch.hpp"
#include "type_id.h"

template<typename T>
void test_serialization( const T &val, const std::string &s )
{
CAPTURE( val );
{
INFO( "test_serialization" );
std::ostringstream os;
JsonOut jsout( os );
jsout.write( val );
CHECK( os.str() == s );
}
{
INFO( "test_deserialization" );
std::istringstream is( s );
JsonIn jsin( is );
T read_val;
CHECK( jsin.read( read_val ) );
CHECK( val == read_val );
}
}

TEST_CASE( "serialize_colony", "[json]" )
{
cata::colony<std::string> c = { "foo", "bar" };
test_serialization( c, R"(["foo","bar"])" );
}

TEST_CASE( "serialize_map", "[json]" )
{
std::map<std::string, std::string> s_map = { { "foo", "foo_val" }, { "bar", "bar_val" } };
test_serialization( s_map, R"({"bar":"bar_val","foo":"foo_val"})" );
std::map<mtype_id, std::string> string_id_map = { { mtype_id( "foo" ), "foo_val" } };
test_serialization( string_id_map, R"({"foo":"foo_val"})" );
std::map<body_part, std::string> enum_map = { { bp_foot_l, "foo_val" } };
test_serialization( enum_map, R"({"FOOT_L":"foo_val"})" );
}

TEST_CASE( "serialize_pair", "[json]" )
{
std::pair<std::string, int> p = { "foo", 42 };
test_serialization( p, R"(["foo",42])" );
}

TEST_CASE( "serialize_sequences", "[json]" )
{
std::vector<std::string> v = { "foo", "bar" };
test_serialization( v, R"(["foo","bar"])" );
std::array<std::string, 2> a = {{ "foo", "bar" }};
test_serialization( a, R"(["foo","bar"])" );
std::list<std::string> l = { "foo", "bar" };
test_serialization( l, R"(["foo","bar"])" );
}

TEST_CASE( "serialize_set", "[json]" )
{
std::set<std::string> s_set = { "foo", "bar" };
test_serialization( s_set, R"(["bar","foo"])" );
std::set<mtype_id> string_id_set = { mtype_id( "foo" ) };
test_serialization( string_id_set, R"(["foo"])" );
std::set<body_part> enum_set = { bp_foot_l };
test_serialization( enum_set, string_format( R"([%d])", static_cast<int>( bp_foot_l ) ) );
}

0 comments on commit 33d3060

Please sign in to comment.