summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/codec-json/c++/json.hpp17
-rw-r--r--modules/codec-json/c++/json.tmpl.hpp61
-rw-r--r--modules/codec-json/tests/codec-json.cpp50
3 files changed, 100 insertions, 28 deletions
diff --git a/modules/codec-json/c++/json.hpp b/modules/codec-json/c++/json.hpp
index 1da74b1..a982bf3 100644
--- a/modules/codec-json/c++/json.hpp
+++ b/modules/codec-json/c++/json.hpp
@@ -66,6 +66,12 @@ struct codec_config<encode::Json> {
uint64_t depth = 16u;
uint64_t length = 4096u;
bool pretty = false;
+ /**
+ * Strict decoding demands a fully described dataset.
+ * Every member of a Struct has to be present in the json data.
+ * Non strict decoding is the default, since configuration files
+ * usually only describe the deviations from the default values.
+ */
bool strict = false;
bool is_within(uint64_t bytes, uint64_t d){
@@ -117,7 +123,7 @@ public:
buffer_view buff_v{from_decode.get_buffer()};
impl::json_helper::skip_whitespace(buff_v);
- auto eov = impl::json_decode<Schema, ToEncoding>::decode(buff_v, to_decode);
+ auto eov = impl::json_decode<Schema, ToEncoding>::decode(buff_v, to_decode, cfg_);
if(eov.is_error()){
return std::move(eov.get_error());
}
@@ -129,6 +135,15 @@ public:
void set_pretty(bool pretty){
cfg_.pretty = pretty;
}
+
+ /**
+ * Strict decoding demands a fully described dataset.
+ * Non strict decoding allows missing struct members, which keep
+ * their default value in that case.
+ */
+ void set_strict(bool strict){
+ cfg_.strict = strict;
+ }
};
}
diff --git a/modules/codec-json/c++/json.tmpl.hpp b/modules/codec-json/c++/json.tmpl.hpp
index 4c6b2f0..a2faf08 100644
--- a/modules/codec-json/c++/json.tmpl.hpp
+++ b/modules/codec-json/c++/json.tmpl.hpp
@@ -489,7 +489,7 @@ template<typename T, size_t N, typename ToDecode>
struct json_decode<schema::Primitive<T,N>, ToDecode> {
using Schema = schema::Primitive<T,N>;
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to) {
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>&) {
assert(
( buff.read() >= '0' && buff.read() <= '9')
|| ( buff.read() == '+' || buff.read() == '-')
@@ -580,7 +580,7 @@ template<typename ToDecode>
struct json_decode<schema::Bool, ToDecode> {
using Schema = schema::Bool;
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>&){
SAW_ASSERT(buff.read() == 't' || buff.read() == 'f'){
return make_error<err::invalid_state>("Invalid JSON boolean");
}
@@ -609,7 +609,7 @@ template<typename ToDecode>
struct json_decode<schema::String, ToDecode> {
using Schema = schema::String;
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>&){
assert(buff.read() == '"');
buff.read_advance(1);
@@ -697,7 +697,7 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
using Schema = schema::Struct<schema::Member<T,Lits>...>;
template<std::size_t i>
- static error_or<void> decode_field_search(buffer_view& buff, data<Schema, ToDecode>& to, std::array<bool, sizeof...(T)>& fields, const data<schema::String,ToDecode>& search_name){
+ static error_or<void> decode_field_search(buffer_view& buff, data<Schema, ToDecode>& to, std::array<bool, sizeof...(T)>& fields, const data<schema::String,ToDecode>& search_name, const codec_config<encode::Json>& cfg){
if constexpr ( i < sizeof...(T)){
using Type = typename parameter_pack_type<i, T...>::type;
constexpr static string_literal Literal = parameter_key_pack_type<i, Lits...>::literal;
@@ -706,12 +706,15 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
return make_error<err::invalid_state>("Duplicate entry in Struct");
}
fields[i] = true;
- auto eov = json_decode<Type, ToDecode>::decode(buff, to.template get<Literal>());
+ auto eov = json_decode<Type, ToDecode>::decode(buff, to.template get<Literal>(), cfg);
if(eov.is_error()){
return eov;
}
}else {
- decode_field_search<i+1>(buff, to, fields, search_name);
+ auto eov = decode_field_search<i+1>(buff, to, fields, search_name, cfg);
+ if(eov.is_error()){
+ return eov;
+ }
}
}else {
return make_error<err::invalid_state>("Field in Struct not found");
@@ -719,13 +722,13 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
return void_t{};
}
- static error_or<void> decode_fields(buffer_view& buff, data<Schema, ToDecode>& to, std::array<bool, sizeof...(T)>& fields){
+ static error_or<void> decode_fields(buffer_view& buff, data<Schema, ToDecode>& to, std::array<bool, sizeof...(T)>& fields, const codec_config<encode::Json>& cfg){
for(;;){
/**
* Decode the member name/key
*/
data<schema::String, ToDecode> name;
- auto eov = json_decode<schema::String, ToDecode>::decode(buff, name);
+ auto eov = json_decode<schema::String, ToDecode>::decode(buff, name, cfg);
if(eov.is_error()){
return eov;
}
@@ -748,7 +751,7 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
* Decode the field value
*/
{
- auto eov = decode_field_search<0>(buff, to, fields, name);
+ auto eov = decode_field_search<0>(buff, to, fields, name, cfg);
if(eov.is_error()){
return eov;
}
@@ -763,10 +766,14 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
if(buff.read() == ','){
buff.read_advance(1);
}else if(buff.read() == '}'){
- // If not all fields are set, the dataset is incomplete
- for(auto& iter : fields){
- if(!iter){
- return make_error<err::invalid_state>("Not all fields were set");
+ // If not all fields are set, the dataset is incomplete.
+ // Non strict decoding leaves the missing members at their
+ // current/default value instead.
+ if(cfg.strict){
+ for(auto& iter : fields){
+ if(!iter){
+ return make_error<err::invalid_state>("Not all fields were set");
+ }
}
}
buff.read_advance(1);
@@ -784,7 +791,7 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
return void_t{};
}
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>& cfg){
std::array<bool, sizeof...(T)> found_fields;
std::fill(found_fields.begin(), found_fields.end(), false);
@@ -799,14 +806,14 @@ struct json_decode<schema::Struct<schema::Member<T,Lits>...>, ToDecode> {
// Check if there are no elements present in the JSON Struct
if(buff.read() == '}'){
- if(sizeof...(T) > 0){
+ if(sizeof...(T) > 0 && cfg.strict){
return make_error<err::invalid_state>("Expected non empty struct");
}
buff.read_advance(1);
return void_t{};
}
- auto eov = decode_fields(buff, to, found_fields);
+ auto eov = decode_fields(buff, to, found_fields, cfg);
if(eov.is_error()){
return eov;
}
@@ -820,7 +827,7 @@ struct json_decode<schema::Tuple<T...>, ToDecode> {
using Schema = schema::Tuple<T...>;
template<std::size_t i>
- static error_or<void> decode_element(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode_element(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>& cfg){
if constexpr (i < sizeof...(T)){
if constexpr ( i > 0 ){
if(buff.read() != ','){
@@ -834,7 +841,7 @@ struct json_decode<schema::Tuple<T...>, ToDecode> {
}
using Type = typename parameter_pack_type<i, T...>::type;
- auto eov = json_decode<Type, ToDecode>::decode(buff, to.template get<i>());
+ auto eov = json_decode<Type, ToDecode>::decode(buff, to.template get<i>(), cfg);
if(eov.is_error()){
return eov;
}
@@ -843,7 +850,7 @@ struct json_decode<schema::Tuple<T...>, ToDecode> {
return make_error<err::buffer_exhausted>();
}
- eov = decode_element<i+1>(buff, to);
+ eov = decode_element<i+1>(buff, to, cfg);
if(eov.is_error()){
return eov;
}
@@ -856,7 +863,7 @@ struct json_decode<schema::Tuple<T...>, ToDecode> {
return void_t{};
}
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>& cfg){
assert(buff.read() == '[');
buff.read_advance(1);
@@ -865,7 +872,7 @@ struct json_decode<schema::Tuple<T...>, ToDecode> {
return make_error<err::buffer_exhausted>();
}
- auto eov = decode_element<0>(buff, to);
+ auto eov = decode_element<0>(buff, to, cfg);
if(eov.is_error()){
return eov;
}
@@ -880,7 +887,7 @@ struct json_decode<schema::Array<T,D>, ToDecode> {
using Schema = schema::Array<T,D>;
template<size_t Level>
- static error_or<void> decode_flat_level(buffer_view& buff, std::vector<data<T, encode::Native>>& to, data<schema::FixedArray<schema::UInt64,D>, ToDecode>& index, data<schema::FixedArray<schema::UInt64,D>, ToDecode>& dims, bool log_dim){
+ static error_or<void> decode_flat_level(buffer_view& buff, std::vector<data<T, encode::Native>>& to, data<schema::FixedArray<schema::UInt64,D>, ToDecode>& index, data<schema::FixedArray<schema::UInt64,D>, ToDecode>& dims, bool log_dim, const codec_config<encode::Json>& cfg){
if constexpr (Level == D) {
json_helper::skip_whitespace(buff);
@@ -889,7 +896,7 @@ struct json_decode<schema::Array<T,D>, ToDecode> {
}catch(std::exception& e){
return make_error<err::out_of_memory>();
}
- auto eov = json_decode<T, ToDecode>::decode(buff, to.back());
+ auto eov = json_decode<T, ToDecode>::decode(buff, to.back(), cfg);
if(eov.is_error()){
return eov;
}
@@ -913,7 +920,7 @@ struct json_decode<schema::Array<T,D>, ToDecode> {
index.at(Level) = 0;
for(;!is_empty;){
// We should have an element right now
- auto eov = decode_flat_level<Level+1>(buff,to,index,dims, index.at(Level).get() == 0 && log_dim);
+ auto eov = decode_flat_level<Level+1>(buff,to,index,dims, index.at(Level).get() == 0 && log_dim, cfg);
if(eov.is_error()){
return eov;
}
@@ -964,11 +971,11 @@ struct json_decode<schema::Array<T,D>, ToDecode> {
return void_t{};
}
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>& cfg){
data<schema::FixedArray<schema::UInt64,D>, ToDecode> index;
data<schema::FixedArray<schema::UInt64,D>, ToDecode> dims;
std::vector<data<T,encode::Native>> flat_array;
- auto eov = decode_flat_level<0>(buff, flat_array, index, dims, true);
+ auto eov = decode_flat_level<0>(buff, flat_array, index, dims, true, cfg);
if(eov.is_error()){
return eov;
}
@@ -984,7 +991,7 @@ template<typename T, uint64_t... Dim, typename ToDecode>
struct json_decode<schema::FixedArray<T,Dim...>, ToDecode> {
using Schema = schema::FixedArray<T,Dim...>;
- static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to){
+ static error_or<void> decode(buffer_view& buff, data<Schema, ToDecode>& to, const codec_config<encode::Json>&){
return make_error<err::not_implemented>("Json FixedArray decoding not implemented.");
}
diff --git a/modules/codec-json/tests/codec-json.cpp b/modules/codec-json/tests/codec-json.cpp
index ab33f5a..a87b237 100644
--- a/modules/codec-json/tests/codec-json.cpp
+++ b/modules/codec-json/tests/codec-json.cpp
@@ -323,6 +323,56 @@ SAW_TEST("Struct read and write"){
SAW_EXPECT(native.get<"bar">() == "baz", "Invalid value for bar");
}
+SAW_TEST("Struct read with missing member"){
+ using namespace saw;
+ data<schema::TestStruct, encode::Native> native;
+ data<schema::TestStruct, encode::Json> json{"{\"bar\":\"baz\"}"};
+
+ codec<schema::TestStruct, encode::Json> json_codec;
+
+ error_or<void> eov = json_codec.decode(json, native);
+ SAW_EXPECT(eov.is_value(), "Decoding error");
+ SAW_EXPECT(native.get<"foo">().get() == 0, "Missing member foo should keep its default value");
+ SAW_EXPECT(native.get<"bar">() == "baz", "Invalid value for bar");
+}
+
+SAW_TEST("Struct read with missing member in strict mode"){
+ using namespace saw;
+ data<schema::TestStruct, encode::Native> native;
+ data<schema::TestStruct, encode::Json> json{"{\"bar\":\"baz\"}"};
+
+ codec_config<encode::Json> json_config;
+ json_config.strict = true;
+ codec<schema::TestStruct, encode::Json> json_codec{json_config};
+
+ error_or<void> eov = json_codec.decode(json, native);
+ SAW_EXPECT(eov.is_error(), "Strict decoding should reject incomplete structs");
+}
+
+SAW_TEST("Empty Struct read"){
+ using namespace saw;
+ data<schema::TestStruct, encode::Native> native;
+ data<schema::TestStruct, encode::Json> json{"{}"};
+
+ codec<schema::TestStruct, encode::Json> json_codec;
+
+ error_or<void> eov = json_codec.decode(json, native);
+ SAW_EXPECT(eov.is_value(), "Decoding error");
+ SAW_EXPECT(native.get<"foo">().get() == 0, "Missing member foo should keep its default value");
+ SAW_EXPECT(native.get<"bar">() == "", "Missing member bar should keep its default value");
+}
+
+SAW_TEST("Struct read with unknown member"){
+ using namespace saw;
+ data<schema::TestStruct, encode::Native> native;
+ data<schema::TestStruct, encode::Json> json{"{\"foo\":5,\"baz\":3,\"bar\":\"baz\"}"};
+
+ codec<schema::TestStruct, encode::Json> json_codec;
+
+ error_or<void> eov = json_codec.decode(json, native);
+ SAW_EXPECT(eov.is_error(), "Unknown members are not supported");
+}
+
SAW_TEST("Array Struct Empty read and write"){
using namespace saw;
data<schema::TestArrayStruct, encode::Native> native{0u};