Serialization: Correctly encode structs with no fields
Empty structs were being serialized as `null`, when they should have been serialized as `{}`. This was due to the type inference on the serializer - where no calls to `field()` would result in the default `null` type. To solve this, the `serialize(const void* object, const std::initializer_list<Field>&)` inline helper has been promoted to a virtual function (and renamed to `fields()`). The JSON serializer implementation of this now first sets the object type to `object`, even if there are no fields to serialize. Added test for this. Fixes: #10
This commit is contained in:
parent
3a10d4cabd
commit
cdc19ac4d9
@ -166,6 +166,10 @@ class Serializer {
|
||||
// Serializer that should be used to encode the n'th array element's data.
|
||||
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
|
||||
|
||||
// fields() encodes all the provided fields of the given object.
|
||||
virtual bool fields(const void* object,
|
||||
const std::initializer_list<Field>&) = 0;
|
||||
|
||||
// field() encodes a field to the struct object referenced by this Serializer.
|
||||
// The FieldSerializer will be called with a Serializer used to encode the
|
||||
// field's data.
|
||||
@ -192,10 +196,6 @@ class Serializer {
|
||||
template <typename T0, typename... Types>
|
||||
inline bool serialize(const dap::variant<T0, Types...>&);
|
||||
|
||||
// serialize() encodes all the provided fields of the given object.
|
||||
inline bool serialize(const void* object,
|
||||
const std::initializer_list<Field>&);
|
||||
|
||||
// deserialize() encodes the given string.
|
||||
inline bool serialize(const char* v);
|
||||
|
||||
@ -231,18 +231,6 @@ bool Serializer::serialize(const dap::variant<T0, Types...>& var) {
|
||||
return serialize(var.value);
|
||||
}
|
||||
|
||||
bool Serializer::serialize(const void* object,
|
||||
const std::initializer_list<Field>& fields) {
|
||||
for (auto const& f : fields) {
|
||||
if (!field(f.name, [&](Serializer* d) {
|
||||
auto ptr = reinterpret_cast<const uint8_t*>(object) + f.offset;
|
||||
return f.type->serialize(d, ptr);
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Serializer::serialize(const char* v) {
|
||||
return serialize(std::string(v));
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ M member_type(M T::*);
|
||||
return d->deserialize(ptr, {__VA_ARGS__}); \
|
||||
} \
|
||||
bool serialize(Serializer* s, const void* ptr) const override { \
|
||||
return s->serialize(ptr, {__VA_ARGS__}); \
|
||||
return s->fields(ptr, {__VA_ARGS__}); \
|
||||
} \
|
||||
}; \
|
||||
static TI typeinfo; \
|
||||
|
@ -224,6 +224,19 @@ bool Serializer::array(size_t count,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Serializer::fields(const void* object,
|
||||
const std::initializer_list<Field>& fields) {
|
||||
*json = nlohmann::json({}, false, nlohmann::json::value_t::object);
|
||||
for (auto const& f : fields) {
|
||||
if (!field(f.name, [&](dap::Serializer* d) {
|
||||
auto ptr = reinterpret_cast<const uint8_t*>(object) + f.offset;
|
||||
return f.type->serialize(d, ptr);
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Serializer::field(const std::string& name,
|
||||
const std::function<bool(dap::Serializer*)>& cb) {
|
||||
Serializer s(&(*json)[name]);
|
||||
|
@ -98,6 +98,8 @@ struct Serializer : public dap::Serializer {
|
||||
bool serialize(const any& v) override;
|
||||
bool array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>&) override;
|
||||
bool fields(const void* object,
|
||||
const std::initializer_list<Field>& fields) override;
|
||||
bool field(const std::string& name, const FieldSerializer&) override;
|
||||
void remove() override;
|
||||
|
||||
@ -130,10 +132,6 @@ struct Serializer : public dap::Serializer {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
inline bool serialize(const void* o, const std::initializer_list<Field>& f) {
|
||||
return dap::Serializer::serialize(o, f);
|
||||
}
|
||||
|
||||
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
|
||||
|
||||
private:
|
||||
|
@ -56,7 +56,9 @@ DAP_STRUCT_TYPEINFO(JSONTestObject,
|
||||
DAP_FIELD(o2, "o2"),
|
||||
DAP_FIELD(inner, "inner"));
|
||||
|
||||
TEST(JSONSerializer, Decode) {}
|
||||
struct JSONObjectNoFields {};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields");
|
||||
|
||||
} // namespace dap
|
||||
|
||||
@ -91,3 +93,10 @@ TEST(JSONSerializer, SerializeDeserialize) {
|
||||
ASSERT_EQ(encoded.o2, decoded.o2);
|
||||
ASSERT_EQ(encoded.inner.i, decoded.inner.i);
|
||||
}
|
||||
|
||||
TEST(JSONSerializer, SerializeObjectNoFields) {
|
||||
dap::JSONObjectNoFields obj;
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(obj));
|
||||
ASSERT_EQ(s.dump(), "{}");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user