blob: b5a5158e2b18e8286ae2f982069a4f76cd8dc300 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "paimon/common/utils/jsonizable.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paimon/testing/utils/testharness.h"
#include "rapidjson/allocators.h"
#include "rapidjson/document.h"
#include "rapidjson/rapidjson.h"
namespace paimon::test {
TEST(JsonizableTest, TestNestedClass) {
class ClassA : public Jsonizable<ClassA> {
public:
bool operator==(const ClassA& other) const {
return vec_ == other.vec_ && string_ == other.string_ && map_ == other.map_;
}
rapidjson::Value ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) override {
rapidjson::Value value(rapidjson::kObjectType);
value.AddMember("vec", RapidJsonUtil::SerializeValue(vec_, allocator).Move(),
*allocator);
value.AddMember("string", RapidJsonUtil::SerializeValue(string_, allocator).Move(),
*allocator);
value.AddMember("map_a", RapidJsonUtil::SerializeValue(map_, allocator).Move(),
*allocator);
return value;
}
void FromJson(const rapidjson::Value& value) noexcept(false) override {
vec_ = RapidJsonUtil::DeserializeKeyValue<std::vector<double>>(value, "vec", vec_);
string_ = RapidJsonUtil::DeserializeKeyValue<std::string>(value, "string", string_);
map_ = RapidJsonUtil::DeserializeKeyValue<std::map<std::string, std::string>>(
value, "map_a", map_);
}
private:
JSONIZABLE_FRIEND_AND_DEFAULT_CTOR(ClassA);
std::vector<double> vec_;
std::string string_;
std::map<std::string, std::string> map_;
};
class ClassB : public Jsonizable<ClassB> {
public:
bool operator==(const ClassB& other) const {
return a_ == other.a_ && a_vec_ == other.a_vec_ && f_ == other.f_ && map_ == other.map_;
}
rapidjson::Value ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) override {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember("ClassA", RapidJsonUtil::SerializeValue(a_, allocator).Move(),
*allocator);
obj.AddMember("ClassA_vec", RapidJsonUtil::SerializeValue(a_vec_, allocator).Move(),
*allocator);
obj.AddMember("float", RapidJsonUtil::SerializeValue(f_, allocator).Move(), *allocator);
obj.AddMember("map_b", RapidJsonUtil::SerializeValue(map_, allocator).Move(),
*allocator);
return obj;
}
void FromJson(const rapidjson::Value& obj) noexcept(false) override {
a_ = RapidJsonUtil::DeserializeKeyValue<ClassA>(obj, "ClassA", a_);
a_vec_ =
RapidJsonUtil::DeserializeKeyValue<std::vector<ClassA>>(obj, "ClassA_vec", a_vec_);
f_ = RapidJsonUtil::DeserializeKeyValue<float>(obj, "float", f_);
map_ = RapidJsonUtil::DeserializeKeyValue<std::map<std::string, std::vector<int>>>(
obj, "map_b", map_);
}
private:
JSONIZABLE_FRIEND_AND_DEFAULT_CTOR(ClassB);
ClassA a_;
std::vector<ClassA> a_vec_;
float f_;
std::map<std::string, std::vector<int>> map_;
};
ClassA obj_a1, obj_a2;
obj_a1.vec_ = {11.0, 12.0, 13.0, 14.0};
obj_a1.string_ = "string_value_1";
obj_a1.map_ = {{"10", "a1"}, {"11", "b1"}, {"12", "c1"}};
obj_a2.vec_ = {21.0, 22.0, 23.0, 24.0};
obj_a2.string_ = "string_value_2";
obj_a2.map_ = {{"20", "a2"}, {"21", "b2"}, {"22", "c2"}};
ClassB obj_b;
obj_b.a_.vec_ = {1.0, 2.0, 3.0, 4.0};
obj_b.a_.string_ = "string_value";
obj_b.a_.map_ = {{"0", "a"}, {"1", "b"}, {"2", "c"}};
obj_b.a_vec_.push_back(obj_a1);
obj_b.a_vec_.push_back(obj_a2);
obj_b.f_ = 10.5;
obj_b.map_ = {{"aa", {0, 1}}, {"bb", {1, 2}}, {"cc", {2, 3}}};
ASSERT_OK_AND_ASSIGN(std::string json_str, obj_b.ToJsonString());
ASSERT_OK_AND_ASSIGN(ClassB obj_b_2, ClassB::FromJsonString(json_str));
ASSERT_EQ(obj_b, obj_b_2);
// test invalid json_str
auto invalid_json_str = json_str.substr(0, json_str.length() / 2);
ASSERT_NOK_WITH_MSG(ClassB::FromJsonString(invalid_json_str), "deserialize failed");
}
TEST(JsonizableTest, TestUpgradeClass) {
class ClassA : public Jsonizable<ClassA> {
public:
bool operator==(const ClassA& other) const {
return vec_ == other.vec_ && string_ == other.string_;
}
rapidjson::Value ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) override {
rapidjson::Value value(rapidjson::kObjectType);
value.AddMember("vec", RapidJsonUtil::SerializeValue(vec_, allocator).Move(),
*allocator);
value.AddMember("string", RapidJsonUtil::SerializeValue(string_, allocator).Move(),
*allocator);
return value;
}
void FromJson(const rapidjson::Value& value) noexcept(false) override {
vec_ = RapidJsonUtil::DeserializeKeyValue<std::vector<double>>(value, "vec", vec_);
string_ = RapidJsonUtil::DeserializeKeyValue<std::string>(value, "string", string_);
}
private:
JSONIZABLE_FRIEND_AND_DEFAULT_CTOR(ClassA);
std::vector<double> vec_;
std::string string_;
};
// modify vec_ from vector<double> to vector<string>
class NewClassA : public Jsonizable<NewClassA> {
public:
rapidjson::Value ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) override {
rapidjson::Value value(rapidjson::kObjectType);
value.AddMember("vec", RapidJsonUtil::SerializeValue(vec_, allocator).Move(),
*allocator);
value.AddMember("string", RapidJsonUtil::SerializeValue(string_, allocator).Move(),
*allocator);
return value;
}
void FromJson(const rapidjson::Value& value) noexcept(false) override {
vec_ = RapidJsonUtil::DeserializeKeyValue<std::vector<std::string>>(value, "vec", vec_);
string_ = RapidJsonUtil::DeserializeKeyValue<std::string>(value, "string", string_);
}
private:
JSONIZABLE_FRIEND_AND_DEFAULT_CTOR(NewClassA);
std::vector<std::string> vec_;
std::string string_;
};
ClassA obj_a;
obj_a.vec_ = {1, 2, 3};
obj_a.string_ = "abcd";
ASSERT_OK_AND_ASSIGN(std::string json_str, obj_a.ToJsonString());
ASSERT_OK_AND_ASSIGN(ClassA obj_a_2, ClassA::FromJsonString(json_str));
ASSERT_EQ(obj_a, obj_a_2);
// test serialize with ClassA and deserialize with NewClassA
ASSERT_NOK_WITH_MSG(NewClassA::FromJsonString(json_str), "value must be string");
}
} // namespace paimon::test