mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 09:58:54 +00:00
Support of nested type in schemer json formatter (#5547)
Summary: Pull Request resolved: https://github.com/facebook/osquery/pull/5547 Since this diff an object of a class with defined schema (see type trait schemer::has_schema) are allowed as memebers of anoter class with schema. Example. C++ classes: ``` class Simple { int alpha = 1; public: template <typename Archive, typename ValueType> static void discloseSchema(Archive& a, ValueType& value) { schemer::record(a, "alpha", value.alpha); } }; class Nested { Frist beta; int gama = 2; public: template <typename Archive, typename ValueType> static void discloseSchema(Archive& a, ValueType& value) { schemer::record(a, "beta", value.beta); schemer::record(a, "gama", value.gama); } }; ``` Json representation of `Nested`: ``` { "beta": { "alpha": 1 }, "gama": 2 } ``` Reviewed By: SAlexandru Differential Revision: D14683589 fbshipit-source-id: 1f9e2f862d2bf64be166a717e49cf0f470f8ee36
This commit is contained in:
parent
eca9296a88
commit
5ec514e714
@ -32,10 +32,10 @@ namespace schemer {
|
||||
* - integral types (int, short, long, long long, unsigned etc);
|
||||
* - floating point nubmers (float, double);
|
||||
* - std::string;
|
||||
* - C-string - only for serialisation.
|
||||
* - C-string - only for serialisation;
|
||||
* - types with defined schema, @see schemer::has_schema type trait.
|
||||
*
|
||||
* Not implemented yet, but comming soon:
|
||||
* - Nested types support
|
||||
* - Standard sontainers support
|
||||
*/
|
||||
|
||||
@ -72,8 +72,6 @@ Expected<std::string, JsonError> toJson(Type const& value) {
|
||||
*/
|
||||
template <typename Type, typename RapidJsonInStream>
|
||||
ExpectedSuccess<JsonError> fromJson(Type& value, RapidJsonInStream& is) {
|
||||
static_assert(!std::is_const<Type>::value,
|
||||
"schemer can read only to non-const ref");
|
||||
auto dom = rapidjson::Document{};
|
||||
dom.ParseStream(is);
|
||||
if (dom.HasParseError()) {
|
||||
@ -83,14 +81,10 @@ ExpectedSuccess<JsonError> fromJson(Type& value, RapidJsonInStream& is) {
|
||||
<< GetParseError_En(dom.GetParseError())
|
||||
<< " Offset: " << dom.GetErrorOffset();
|
||||
}
|
||||
if (!dom.IsObject()) {
|
||||
return createError(JsonError::TypeMismatch)
|
||||
<< "Can not parse value of type "
|
||||
<< boost::core::demangle(typeid(Type).name())
|
||||
<< " from JSON. Incorrect type, expected object";
|
||||
}
|
||||
auto reader = impl::JsonReader{dom};
|
||||
Type::discloseSchema(reader, value);
|
||||
if (reader.status.isValue()) {
|
||||
Type::discloseSchema(reader, value);
|
||||
}
|
||||
if (reader.status.isError()) {
|
||||
return createError(JsonError::IncorrectFormat, reader.status.takeError())
|
||||
<< "Can not parse value of type "
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <osquery/utils/json/json.h>
|
||||
#include <osquery/utils/schemer/schemer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace osquery {
|
||||
@ -82,6 +83,17 @@ void writeValue(WriterType& writer, ValueType const& value) {
|
||||
writer.String(value);
|
||||
}
|
||||
|
||||
template <typename WriterType>
|
||||
class JsonWriter;
|
||||
|
||||
template <typename WriterType,
|
||||
typename ValueType,
|
||||
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
|
||||
void writeValue(WriterType& writer, ValueType const& value) {
|
||||
auto next_writer = impl::JsonWriter<WriterType>(writer);
|
||||
ValueType::discloseSchema(next_writer, value);
|
||||
}
|
||||
|
||||
template <typename WriterType>
|
||||
class JsonWriter final {
|
||||
public:
|
||||
@ -107,6 +119,12 @@ class JsonReader final {
|
||||
public:
|
||||
explicit JsonReader(rapidjson::Value const& jObject) : jObject_(jObject) {
|
||||
status.ignoreResult();
|
||||
if (!jObject_.IsObject()) {
|
||||
status =
|
||||
createError(JsonError::TypeMismatch)
|
||||
<< "Wrong type of value: " << jValueToStringForErrorMessage(jObject_)
|
||||
<< ", expected object";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
@ -189,6 +207,24 @@ class JsonReader final {
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename KeyType,
|
||||
typename ValueType,
|
||||
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
|
||||
void copyValueFromJValue(const KeyType& key,
|
||||
ValueType& value,
|
||||
rapidjson::Value const& jValue) {
|
||||
auto next_reader = impl::JsonReader{jValue};
|
||||
if (next_reader.status.isError()) {
|
||||
status = std::move(next_reader.status);
|
||||
} else {
|
||||
ValueType::discloseSchema(next_reader, value);
|
||||
if (next_reader.status.isError()) {
|
||||
status = std::move(next_reader.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ExpectedSuccess<JsonError> status = Success{};
|
||||
|
||||
|
@ -31,7 +31,7 @@ class TestClass {
|
||||
schemer::record(a, "Sierra", value.str_v_);
|
||||
}
|
||||
|
||||
private:
|
||||
public:
|
||||
bool b_v_ = true;
|
||||
int i_v_ = -92374;
|
||||
int ui_v_ = 64774;
|
||||
@ -57,6 +57,28 @@ TEST_F(SchemerJsonTests, writer_to_string) {
|
||||
R"json({"Bravo":true,"India":-92374,"Uniform":64774,"Sierra":"What is Architecture?"})json");
|
||||
}
|
||||
|
||||
class NestedTestClass {
|
||||
public:
|
||||
template <typename Archive, typename ValueType>
|
||||
static void discloseSchema(Archive& a, ValueType& value) {
|
||||
schemer::record(a, "First", value.first_);
|
||||
schemer::record(a, "test_class", value.second_);
|
||||
}
|
||||
|
||||
public:
|
||||
int first_ = -273;
|
||||
TestClass second_;
|
||||
};
|
||||
|
||||
TEST_F(SchemerJsonTests, writer_nested_to_string) {
|
||||
auto const v = NestedTestClass{};
|
||||
auto const exp = schemer::toJson(v);
|
||||
EXPECT_TRUE(exp) << exp.getError().getMessage();
|
||||
EXPECT_EQ(
|
||||
exp.get(),
|
||||
R"json({"First":-273,"test_class":{"Bravo":true,"India":-92374,"Uniform":64774,"Sierra":"What is Architecture?"}})json");
|
||||
}
|
||||
|
||||
class SecondTestClass {
|
||||
public:
|
||||
template <typename Archive, typename ValueType>
|
||||
@ -128,7 +150,7 @@ TEST_F(SchemerJsonTests, read_from_stream_object_type_error) {
|
||||
])json"};
|
||||
auto const retcode = schemer::fromJson(v, buf);
|
||||
ASSERT_TRUE(retcode.isError());
|
||||
ASSERT_EQ(retcode.getErrorCode(), schemer::JsonError::TypeMismatch);
|
||||
ASSERT_EQ(retcode.getErrorCode(), schemer::JsonError::IncorrectFormat);
|
||||
}
|
||||
|
||||
TEST_F(SchemerJsonTests, read_from_stream_member_type_error) {
|
||||
@ -196,5 +218,46 @@ TEST_F(SchemerJsonTests, read_write) {
|
||||
EXPECT_EQ(fromValue.fourth, toValue.fourth);
|
||||
}
|
||||
|
||||
TEST_F(SchemerJsonTests, read_nested_from_string) {
|
||||
auto const str =
|
||||
R"json({"First":-459,"test_class":{"Bravo":false,"India":31,"Uniform":145,"Sierra":"I have no clue"}})json";
|
||||
auto value = NestedTestClass{};
|
||||
auto const retcode = schemer::fromJson(value, str);
|
||||
ASSERT_TRUE(retcode.isValue()) << retcode.getError().getMessage();
|
||||
|
||||
EXPECT_EQ(value.first_, -459);
|
||||
EXPECT_EQ(value.second_.b_v_, false);
|
||||
EXPECT_EQ(value.second_.i_v_, 31);
|
||||
EXPECT_EQ(value.second_.ui_v_, 145);
|
||||
EXPECT_EQ(value.second_.str_v_, "I have no clue");
|
||||
}
|
||||
|
||||
TEST_F(SchemerJsonTests,
|
||||
read_nested_from_string_fails_because_value_is_not_an_object) {
|
||||
auto const str = R"json({"First":-459,"test_class":false})json";
|
||||
auto value = NestedTestClass{};
|
||||
auto const retcode = schemer::fromJson(value, str);
|
||||
ASSERT_TRUE(retcode.isError());
|
||||
EXPECT_EQ(retcode.getErrorCode(), schemer::JsonError::IncorrectFormat);
|
||||
}
|
||||
|
||||
TEST_F(SchemerJsonTests,
|
||||
read_nested_from_string_fails_because_of_incomplete_json) {
|
||||
// here is JSON with missed last '}'
|
||||
auto const str = R"json({
|
||||
"First":-459,
|
||||
"test_class":{
|
||||
"Bravo":false,
|
||||
"India":31,
|
||||
"Uniform":145,
|
||||
"Sierra":"I have no clue"
|
||||
}
|
||||
)json";
|
||||
auto value = NestedTestClass{};
|
||||
auto const retcode = schemer::fromJson(value, str);
|
||||
ASSERT_TRUE(retcode.isError());
|
||||
EXPECT_EQ(retcode.getErrorCode(), schemer::JsonError::Syntax);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace osquery
|
||||
|
Loading…
Reference in New Issue
Block a user