From 5a951e19d332afdcd0a0aee6e4194357e770abd7 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 25 Aug 2019 08:04:34 -0400 Subject: [PATCH 1/5] Support serialization of maps with enum keys --- src/json.h | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/json.h b/src/json.h index 1c4ae7abaa7c3..c156a058dd562 100644 --- a/src/json.h +++ b/src/json.h @@ -36,6 +36,9 @@ class JsonArray; class JsonSerializer; class JsonDeserializer; +template +class string_id; + class JsonError : public std::runtime_error { public: @@ -45,6 +48,30 @@ class JsonError : public std::runtime_error } }; +template +struct key_from_json_string; + +template<> +struct key_from_json_string { + std::string operator()( const std::string &s ) { + return s; + } +}; + +template +struct key_from_json_string, void> { + string_id operator()( const std::string &s ) { + return string_id( s ); + } +}; + +template +struct key_from_json_string::value>> { + Enum operator()( const std::string &s ) { + return io::string_to_enum( s ); + } +}; + /* JsonIn * ====== * @@ -381,10 +408,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()( 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(); } @@ -531,6 +559,15 @@ class JsonOut write( io::enum_to_string( value ) ); } + void write_as_string( const std::string &s ) { + write( s ); + } + + template + void write_as_string( const string_id &s ) { + write( s ); + } + template void write_as_array( const T &container ) { start_array(); @@ -572,7 +609,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 ); } From 59ca92351edee203f8b2bed8b75d5634c054ebcf Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 25 Aug 2019 08:27:21 -0400 Subject: [PATCH 2/5] Serialization support for std::pair --- src/json.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/json.h b/src/json.h index c156a058dd562..5d38c4250b7e0 100644 --- a/src/json.h +++ b/src/json.h @@ -296,6 +296,24 @@ class JsonIn return true; } + /// Overload for std::pair + template + bool read( std::pair &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; + } + } + // array ~> vector, deque, list template < typename T, typename std::enable_if < !std::is_same::value >::type * = nullptr @@ -568,6 +586,14 @@ class JsonOut write( s ); } + template + void write( const std::pair &p ) { + start_array(); + write( p.first ); + write( p.second ); + end_array(); + } + template void write_as_array( const T &container ) { start_array(); From d1c4f6f730fbfe4d462d815bd62d97be8905e091 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 1 Sep 2019 14:21:34 -0400 Subject: [PATCH 3/5] Make jsin::read on enums more flexible It will now read enums stored either as strings or ints. This is useful because when they are e.g. inside a container it might not be easy to specify which form they are in. --- src/json.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/json.h b/src/json.h index 5d38c4250b7e0..6acea4bc656c1 100644 --- a/src/json.h +++ b/src/json.h @@ -288,11 +288,19 @@ class JsonIn template::value, int> = 0> bool read( T &val ) { - std::string s; - if( !read( s ) ) { + if( test_string() ) { + std::string s; + if( !read( s ) ) { + return false; + } + val = io::string_to_enum( s ); + return true; + } + int i; + if( !read( i ) ) { return false; } - val = io::string_to_enum( s ); + val = static_cast( i ); return true; } From 624b18306fb9277af5071276a317f28cda0ad6ea Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 1 Sep 2019 14:29:17 -0400 Subject: [PATCH 4/5] Tests for STL container serialization --- tests/json_test.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/json_test.cpp diff --git a/tests/json_test.cpp b/tests/json_test.cpp new file mode 100644 index 0000000000000..032c1bf15ad12 --- /dev/null +++ b/tests/json_test.cpp @@ -0,0 +1,71 @@ +#include "json.h" + +#include +#include + +#include "bodypart.h" +#include "catch/catch.hpp" +#include "type_id.h" + +template +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 c = { "foo", "bar" }; + test_serialization( c, R"(["foo","bar"])" ); +} + +TEST_CASE( "serialize_map", "[json]" ) +{ + std::map s_map = { { "foo", "foo_val" }, { "bar", "bar_val" } }; + test_serialization( s_map, R"({"bar":"bar_val","foo":"foo_val"})" ); + std::map string_id_map = { { mtype_id( "foo" ), "foo_val" } }; + test_serialization( string_id_map, R"({"foo":"foo_val"})" ); + std::map enum_map = { { bp_foot_l, "foo_val" } }; + test_serialization( enum_map, R"({"FOOT_L":"foo_val"})" ); +} + +TEST_CASE( "serialize_pair", "[json]" ) +{ + std::pair p = { "foo", 42 }; + test_serialization( p, R"(["foo",42])" ); +} + +TEST_CASE( "serialize_sequences", "[json]" ) +{ + std::vector v = { "foo", "bar" }; + test_serialization( v, R"(["foo","bar"])" ); + std::array a = {{ "foo", "bar" }}; + test_serialization( a, R"(["foo","bar"])" ); + std::list l = { "foo", "bar" }; + test_serialization( l, R"(["foo","bar"])" ); +} + +TEST_CASE( "serialize_set", "[json]" ) +{ + std::set s_set = { "foo", "bar" }; + test_serialization( s_set, R"(["bar","foo"])" ); + std::set string_id_set = { mtype_id( "foo" ) }; + test_serialization( string_id_set, R"(["foo"])" ); + std::set enum_set = { bp_foot_l }; + test_serialization( enum_set, string_format( R"([%d])", static_cast( bp_foot_l ) ) ); +} From 621b4c19d98c443bc95f20ec74ed2a97e170ba2a Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 1 Sep 2019 20:58:27 -0400 Subject: [PATCH 5/5] Refactor JsonIn::read for enums A cleaner implementations suggested by @BevapDin. --- src/json.h | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/json.h b/src/json.h index 6acea4bc656c1..c843288e42ed0 100644 --- a/src/json.h +++ b/src/json.h @@ -288,20 +288,17 @@ class JsonIn template::value, int> = 0> bool read( T &val ) { - if( test_string() ) { - std::string s; - if( !read( s ) ) { - return false; - } - val = io::string_to_enum( s ); + int i; + if( read( i ) ) { + val = static_cast( i ); return true; } - int i; - if( !read( i ) ) { - return false; + std::string s; + if( read( s ) ) { + val = io::string_to_enum( s ); + return true; } - val = static_cast( i ); - return true; + return false; } /// Overload for std::pair