| /* |
| * 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 |