// Copyright 2020 gRPC authors. // // Licensed 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. #ifndef GRPC_SRC_CORE_LIB_JSON_JSON_OBJECT_LOADER_H #define GRPC_SRC_CORE_LIB_JSON_JSON_OBJECT_LOADER_H #include #include #include #include #include #include #include #include #include "absl/meta/type_traits.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "src/core/lib/gprpp/no_destruct.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/time.h" #include "src/core/lib/gprpp/validation_errors.h" #include "src/core/lib/json/json.h" #include "src/core/lib/json/json_args.h" // Provides a means to load JSON objects into C++ objects, with the aim of // minimizing object code size. // // Usage: // Given struct Foo: // struct Foo { // int a; // int b; // }; // We add a static JsonLoader() method to Foo to declare how to load the // object from JSON, and an optional JsonPostLoad() method to do any // necessary post-processing: // struct Foo { // int a; // int b; // static const JsonLoaderInterface* JsonLoader(const JsonArgs& args) { // // Note: Field names must be string constants; they are not copied. // static const auto* loader = JsonObjectLoader() // .Field("a", &Foo::a) // .Field("b", &Foo::b) // .Finish(); // return loader; // } // // Optional; omit if no post-processing needed. // void JsonPostLoad(const Json& source, const JsonArgs& args, // ValidationErrors* errors) { // ++a; // } // }; // Now we can load Foo objects from JSON: // absl::StatusOr foo = LoadFromJson(json); namespace grpc_core { namespace json_detail { // An un-typed JSON loader. class LoaderInterface { public: // Convert json value to whatever type we're loading at dst. // If errors occur, add them to errors. virtual void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const = 0; protected: ~LoaderInterface() = default; }; // Loads a scalar (string or number). class LoadScalar : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override; protected: ~LoadScalar() = default; private: // true if we're loading a number, false if we're loading a string. // We use a virtual function to store this decision in a vtable instead of // needing an instance variable. virtual bool IsNumber() const = 0; virtual void LoadInto(const std::string& json, void* dst, ValidationErrors* errors) const = 0; }; // Load a string. class LoadString : public LoadScalar { protected: ~LoadString() = default; private: bool IsNumber() const override; void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override; }; // Load a Duration. class LoadDuration : public LoadScalar { protected: ~LoadDuration() = default; private: bool IsNumber() const override; void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override; }; // Load a number. class LoadNumber : public LoadScalar { protected: ~LoadNumber() = default; private: bool IsNumber() const override; }; // Load a signed number of type T. template class TypedLoadSignedNumber : public LoadNumber { protected: ~TypedLoadSignedNumber() = default; private: void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override { if (!absl::SimpleAtoi(value, static_cast(dst))) { errors->AddError("failed to parse number"); } } }; // Load an unsigned number of type T. template class TypedLoadUnsignedNumber : public LoadNumber { protected: ~TypedLoadUnsignedNumber() = default; private: void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override { if (!absl::SimpleAtoi(value, static_cast(dst))) { errors->AddError("failed to parse non-negative number"); } } }; // Load a float. class LoadFloat : public LoadNumber { protected: ~LoadFloat() = default; private: void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override { if (!absl::SimpleAtof(value, static_cast(dst))) { errors->AddError("failed to parse floating-point number"); } } }; // Load a double. class LoadDouble : public LoadNumber { protected: ~LoadDouble() = default; private: void LoadInto(const std::string& value, void* dst, ValidationErrors* errors) const override { if (!absl::SimpleAtod(value, static_cast(dst))) { errors->AddError("failed to parse floating-point number"); } } }; // Load a bool. class LoadBool : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& /*args*/, void* dst, ValidationErrors* errors) const override; protected: ~LoadBool() = default; }; // Loads an unprocessed JSON object value. class LoadUnprocessedJsonObject : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& /*args*/, void* dst, ValidationErrors* errors) const override; protected: ~LoadUnprocessedJsonObject() = default; }; // Loads an unprocessed JSON array value. class LoadUnprocessedJsonArray : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& /*args*/, void* dst, ValidationErrors* errors) const override; protected: ~LoadUnprocessedJsonArray() = default; }; // Load a vector of some type. class LoadVector : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override; protected: ~LoadVector() = default; private: virtual void* EmplaceBack(void* dst) const = 0; virtual const LoaderInterface* ElementLoader() const = 0; }; // Load a map of string->some type. class LoadMap : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override; protected: ~LoadMap() = default; private: virtual void* Insert(const std::string& name, void* dst) const = 0; virtual const LoaderInterface* ElementLoader() const = 0; }; // Load an optional of some type. class LoadOptional : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override; protected: ~LoadOptional() = default; private: virtual void* Emplace(void* dst) const = 0; virtual void Reset(void* dst) const = 0; virtual const LoaderInterface* ElementLoader() const = 0; }; // Fetch a LoaderInterface for some type. template const LoaderInterface* LoaderForType(); // AutoLoader implements LoaderInterface for a type. // The default asks the type for its LoaderInterface and then uses that. // Classes that load from objects should provide a: // static const JsonLoaderInterface* JsonLoader(); template class AutoLoader final : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override { T::JsonLoader(args)->LoadInto(json, args, dst, errors); } private: ~AutoLoader() = default; }; // Specializations of AutoLoader for basic types. template <> class AutoLoader final : public LoadString { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadDuration { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public TypedLoadSignedNumber { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public TypedLoadSignedNumber { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public TypedLoadUnsignedNumber { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public TypedLoadUnsignedNumber { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadFloat { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadDouble { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadBool { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadUnprocessedJsonObject { private: ~AutoLoader() = default; }; template <> class AutoLoader final : public LoadUnprocessedJsonArray { private: ~AutoLoader() = default; }; // Specializations of AutoLoader for vectors. template class AutoLoader> final : public LoadVector { private: ~AutoLoader() = default; void* EmplaceBack(void* dst) const final { auto* vec = static_cast*>(dst); vec->emplace_back(); return &vec->back(); } const LoaderInterface* ElementLoader() const final { return LoaderForType(); } }; // Specialization of AutoLoader for vector - we need a different // implementation because, as vector packs bits in its implementation, the // technique of returning a void* from Emplace() for the generic vector loader // doesn't work. template <> class AutoLoader> final : public LoaderInterface { public: void LoadInto(const Json& json, const JsonArgs& args, void* dst, ValidationErrors* errors) const override; private: ~AutoLoader() = default; }; // Specializations of AutoLoader for maps. template class AutoLoader> final : public LoadMap { private: void* Insert(const std::string& name, void* dst) const final { return &static_cast*>(dst) ->emplace(name, T()) .first->second; }; const LoaderInterface* ElementLoader() const final { return LoaderForType(); } ~AutoLoader() = default; }; // Specializations of AutoLoader for absl::optional<>. template class AutoLoader> final : public LoadOptional { public: void* Emplace(void* dst) const final { return &static_cast*>(dst)->emplace(); } void Reset(void* dst) const final { static_cast*>(dst)->reset(); } const LoaderInterface* ElementLoader() const final { return LoaderForType(); } private: ~AutoLoader() = default; }; // Specializations of AutoLoader for std::unique_ptr<>. template class AutoLoader> final : public LoadOptional { public: void* Emplace(void* dst) const final { auto& p = *static_cast*>(dst); p = std::make_unique(); return p.get(); } void Reset(void* dst) const final { static_cast*>(dst)->reset(); } const LoaderInterface* ElementLoader() const final { return LoaderForType(); } private: ~AutoLoader() = default; }; // Implementation of aforementioned LoaderForType. // Simply keeps a static AutoLoader and returns a pointer to that. template const LoaderInterface* LoaderForType() { return NoDestructSingleton>::Get(); } // Element describes one typed field to be loaded from a JSON object. struct Element { Element() = default; template Element(const char* name, bool optional, B A::*p, const LoaderInterface* loader, const char* enable_key) : loader(loader), member_offset(static_cast( reinterpret_cast(&(static_cast(nullptr)->*p)))), optional(optional), name(name), enable_key(enable_key) {} // The loader for this field. const LoaderInterface* loader; // Offset into the destination object to store the field. uint16_t member_offset; // Is this field optional? bool optional; // The name of the field. const char* name; // The key to use with JsonArgs to see if this field is enabled. const char* enable_key; }; // Vec provides a constant array type that can be appended to by // copying. It's setup so that most compilers can optimize away all of its // operations. template class Vec { public: Vec(const Vec& other, const T& new_value) { for (size_t i = 0; i < other.size(); i++) values_[i] = other.data()[i]; values_[kSize - 1] = new_value; } const T* data() const { return values_; } size_t size() const { return kSize; } private: T values_[kSize]; }; template class Vec { public: const T* data() const { return nullptr; } size_t size() const { return 0; } }; // Given a list of elements, and a destination object, load the elements into // the object from some parsed JSON. // Returns false if the JSON object was not of type Json::Type::kObject. bool LoadObject(const Json& json, const JsonArgs& args, const Element* elements, size_t num_elements, void* dst, ValidationErrors* errors); // Adaptor type - takes a compile time computed list of elements and // implements LoaderInterface by calling LoadObject. template