diff --git a/BUILD b/BUILD
index 88a3d15feb0..3541025a1fd 100644
--- a/BUILD
+++ b/BUILD
@@ -7260,10 +7260,52 @@ grpc_cc_library(
"error",
"gpr_base",
"json",
+ "json_args",
+ "json_object_loader",
"time",
],
)
+grpc_cc_library(
+ name = "json_args",
+ hdrs = ["src/core/lib/json/json_args.h"],
+ external_deps = ["absl/strings"],
+ deps = ["gpr_base"],
+)
+
+grpc_cc_library(
+ name = "json_object_loader",
+ srcs = ["src/core/lib/json/json_object_loader.cc"],
+ hdrs = ["src/core/lib/json/json_object_loader.h"],
+ external_deps = [
+ "absl/meta:type_traits",
+ "absl/status",
+ "absl/status:statusor",
+ "absl/strings",
+ "absl/types:optional",
+ ],
+ deps = [
+ "gpr_base",
+ "json",
+ "json_args",
+ "time",
+ ],
+)
+
+grpc_cc_library(
+ name = "json_channel_args",
+ hdrs = ["src/core/lib/json/json_channel_args.h"],
+ external_deps = [
+ "absl/strings",
+ "absl/types:optional",
+ ],
+ deps = [
+ "channel_args",
+ "gpr",
+ "json_args",
+ ],
+)
+
### UPB Targets
grpc_upb_proto_library(
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e02b8782a6b..2f374f2e687 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1025,6 +1025,7 @@ if(gRPC_BUILD_TESTS)
endif()
add_dependencies(buildtests_cxx istio_echo_server_test)
add_dependencies(buildtests_cxx join_test)
+ add_dependencies(buildtests_cxx json_object_loader_test)
add_dependencies(buildtests_cxx json_test)
add_dependencies(buildtests_cxx json_token_test)
add_dependencies(buildtests_cxx jwt_verifier_test)
@@ -2186,6 +2187,7 @@ add_library(grpc
src/core/lib/iomgr/wakeup_fd_nospecial.cc
src/core/lib/iomgr/wakeup_fd_pipe.cc
src/core/lib/iomgr/wakeup_fd_posix.cc
+ src/core/lib/json/json_object_loader.cc
src/core/lib/json/json_reader.cc
src/core/lib/json/json_util.cc
src/core/lib/json/json_writer.cc
@@ -2794,6 +2796,7 @@ add_library(grpc_unsecure
src/core/lib/iomgr/wakeup_fd_nospecial.cc
src/core/lib/iomgr/wakeup_fd_pipe.cc
src/core/lib/iomgr/wakeup_fd_posix.cc
+ src/core/lib/json/json_object_loader.cc
src/core/lib/json/json_reader.cc
src/core/lib/json/json_util.cc
src/core/lib/json/json_writer.cc
@@ -12623,6 +12626,41 @@ target_link_libraries(join_test
)
+endif()
+if(gRPC_BUILD_TESTS)
+
+add_executable(json_object_loader_test
+ test/core/json/json_object_loader_test.cc
+ third_party/googletest/googletest/src/gtest-all.cc
+ third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(json_object_loader_test
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ ${_gRPC_RE2_INCLUDE_DIR}
+ ${_gRPC_SSL_INCLUDE_DIR}
+ ${_gRPC_UPB_GENERATED_DIR}
+ ${_gRPC_UPB_GRPC_GENERATED_DIR}
+ ${_gRPC_UPB_INCLUDE_DIR}
+ ${_gRPC_XXHASH_INCLUDE_DIR}
+ ${_gRPC_ZLIB_INCLUDE_DIR}
+ third_party/googletest/googletest/include
+ third_party/googletest/googletest
+ third_party/googletest/googlemock/include
+ third_party/googletest/googlemock
+ ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(json_object_loader_test
+ ${_gRPC_PROTOBUF_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc_test_util
+)
+
+
endif()
if(gRPC_BUILD_TESTS)
diff --git a/Makefile b/Makefile
index cf54b0cc1a3..9c902fcd849 100644
--- a/Makefile
+++ b/Makefile
@@ -1548,6 +1548,7 @@ LIBGRPC_SRC = \
src/core/lib/iomgr/wakeup_fd_nospecial.cc \
src/core/lib/iomgr/wakeup_fd_pipe.cc \
src/core/lib/iomgr/wakeup_fd_posix.cc \
+ src/core/lib/json/json_object_loader.cc \
src/core/lib/json/json_reader.cc \
src/core/lib/json/json_util.cc \
src/core/lib/json/json_writer.cc \
@@ -2020,6 +2021,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/iomgr/wakeup_fd_nospecial.cc \
src/core/lib/iomgr/wakeup_fd_pipe.cc \
src/core/lib/iomgr/wakeup_fd_posix.cc \
+ src/core/lib/json/json_object_loader.cc \
src/core/lib/json/json_reader.cc \
src/core/lib/json/json_util.cc \
src/core/lib/json/json_writer.cc \
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 049d1f3d507..42686e382e5 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -849,6 +849,8 @@ libs:
- src/core/lib/iomgr/wakeup_fd_pipe.h
- src/core/lib/iomgr/wakeup_fd_posix.h
- src/core/lib/json/json.h
+ - src/core/lib/json/json_args.h
+ - src/core/lib/json/json_object_loader.h
- src/core/lib/json/json_util.h
- src/core/lib/load_balancing/lb_policy.h
- src/core/lib/load_balancing/lb_policy_factory.h
@@ -1540,6 +1542,7 @@ libs:
- src/core/lib/iomgr/wakeup_fd_nospecial.cc
- src/core/lib/iomgr/wakeup_fd_pipe.cc
- src/core/lib/iomgr/wakeup_fd_posix.cc
+ - src/core/lib/json/json_object_loader.cc
- src/core/lib/json/json_reader.cc
- src/core/lib/json/json_util.cc
- src/core/lib/json/json_writer.cc
@@ -2031,6 +2034,8 @@ libs:
- src/core/lib/iomgr/wakeup_fd_pipe.h
- src/core/lib/iomgr/wakeup_fd_posix.h
- src/core/lib/json/json.h
+ - src/core/lib/json/json_args.h
+ - src/core/lib/json/json_object_loader.h
- src/core/lib/json/json_util.h
- src/core/lib/load_balancing/lb_policy.h
- src/core/lib/load_balancing/lb_policy_factory.h
@@ -2363,6 +2368,7 @@ libs:
- src/core/lib/iomgr/wakeup_fd_nospecial.cc
- src/core/lib/iomgr/wakeup_fd_pipe.cc
- src/core/lib/iomgr/wakeup_fd_posix.cc
+ - src/core/lib/json/json_object_loader.cc
- src/core/lib/json/json_reader.cc
- src/core/lib/json/json_util.cc
- src/core/lib/json/json_writer.cc
@@ -7392,6 +7398,16 @@ targets:
- absl/types:variant
- absl/utility:utility
uses_polling: false
+- name: json_object_loader_test
+ gtest: true
+ build: test
+ language: c++
+ headers: []
+ src:
+ - test/core/json/json_object_loader_test.cc
+ deps:
+ - grpc_test_util
+ uses_polling: false
- name: json_test
gtest: true
build: test
diff --git a/config.m4 b/config.m4
index 6dd509e5a82..06d35812bf1 100644
--- a/config.m4
+++ b/config.m4
@@ -607,6 +607,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/iomgr/wakeup_fd_nospecial.cc \
src/core/lib/iomgr/wakeup_fd_pipe.cc \
src/core/lib/iomgr/wakeup_fd_posix.cc \
+ src/core/lib/json/json_object_loader.cc \
src/core/lib/json/json_reader.cc \
src/core/lib/json/json_util.cc \
src/core/lib/json/json_writer.cc \
diff --git a/config.w32 b/config.w32
index 46cfab92560..26f612c41af 100644
--- a/config.w32
+++ b/config.w32
@@ -573,6 +573,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\iomgr\\wakeup_fd_nospecial.cc " +
"src\\core\\lib\\iomgr\\wakeup_fd_pipe.cc " +
"src\\core\\lib\\iomgr\\wakeup_fd_posix.cc " +
+ "src\\core\\lib\\json\\json_object_loader.cc " +
"src\\core\\lib\\json\\json_reader.cc " +
"src\\core\\lib\\json\\json_util.cc " +
"src\\core\\lib\\json\\json_writer.cc " +
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index fb0d095f70b..59c6c1e61e7 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -807,6 +807,8 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/wakeup_fd_pipe.h',
'src/core/lib/iomgr/wakeup_fd_posix.h',
'src/core/lib/json/json.h',
+ 'src/core/lib/json/json_args.h',
+ 'src/core/lib/json/json_object_loader.h',
'src/core/lib/json/json_util.h',
'src/core/lib/load_balancing/lb_policy.h',
'src/core/lib/load_balancing/lb_policy_factory.h',
@@ -1659,6 +1661,8 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/wakeup_fd_pipe.h',
'src/core/lib/iomgr/wakeup_fd_posix.h',
'src/core/lib/json/json.h',
+ 'src/core/lib/json/json_args.h',
+ 'src/core/lib/json/json_object_loader.h',
'src/core/lib/json/json_util.h',
'src/core/lib/load_balancing/lb_policy.h',
'src/core/lib/load_balancing/lb_policy_factory.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 456bd60d160..7b998c9dc89 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -1312,6 +1312,9 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/wakeup_fd_posix.cc',
'src/core/lib/iomgr/wakeup_fd_posix.h',
'src/core/lib/json/json.h',
+ 'src/core/lib/json/json_args.h',
+ 'src/core/lib/json/json_object_loader.cc',
+ 'src/core/lib/json/json_object_loader.h',
'src/core/lib/json/json_reader.cc',
'src/core/lib/json/json_util.cc',
'src/core/lib/json/json_util.h',
@@ -2280,6 +2283,8 @@ Pod::Spec.new do |s|
'src/core/lib/iomgr/wakeup_fd_pipe.h',
'src/core/lib/iomgr/wakeup_fd_posix.h',
'src/core/lib/json/json.h',
+ 'src/core/lib/json/json_args.h',
+ 'src/core/lib/json/json_object_loader.h',
'src/core/lib/json/json_util.h',
'src/core/lib/load_balancing/lb_policy.h',
'src/core/lib/load_balancing/lb_policy_factory.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 5215d8ca7bf..3e09b77673c 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -1225,6 +1225,9 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/iomgr/wakeup_fd_posix.cc )
s.files += %w( src/core/lib/iomgr/wakeup_fd_posix.h )
s.files += %w( src/core/lib/json/json.h )
+ s.files += %w( src/core/lib/json/json_args.h )
+ s.files += %w( src/core/lib/json/json_object_loader.cc )
+ s.files += %w( src/core/lib/json/json_object_loader.h )
s.files += %w( src/core/lib/json/json_reader.cc )
s.files += %w( src/core/lib/json/json_util.cc )
s.files += %w( src/core/lib/json/json_util.h )
diff --git a/grpc.gyp b/grpc.gyp
index a9b7fae6be7..332a579fff2 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -899,6 +899,7 @@
'src/core/lib/iomgr/wakeup_fd_nospecial.cc',
'src/core/lib/iomgr/wakeup_fd_pipe.cc',
'src/core/lib/iomgr/wakeup_fd_posix.cc',
+ 'src/core/lib/json/json_object_loader.cc',
'src/core/lib/json/json_reader.cc',
'src/core/lib/json/json_util.cc',
'src/core/lib/json/json_writer.cc',
@@ -1339,6 +1340,7 @@
'src/core/lib/iomgr/wakeup_fd_nospecial.cc',
'src/core/lib/iomgr/wakeup_fd_pipe.cc',
'src/core/lib/iomgr/wakeup_fd_posix.cc',
+ 'src/core/lib/json/json_object_loader.cc',
'src/core/lib/json/json_reader.cc',
'src/core/lib/json/json_util.cc',
'src/core/lib/json/json_writer.cc',
diff --git a/package.xml b/package.xml
index e8547b3360c..2a61574e9b9 100644
--- a/package.xml
+++ b/package.xml
@@ -1207,6 +1207,9 @@
+
+
+
diff --git a/src/core/lib/json/json_args.h b/src/core/lib/json/json_args.h
new file mode 100644
index 00000000000..e975d336234
--- /dev/null
+++ b/src/core/lib/json/json_args.h
@@ -0,0 +1,34 @@
+// 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_CORE_LIB_JSON_JSON_ARGS_H
+#define GRPC_CORE_LIB_JSON_JSON_ARGS_H
+
+#include
+
+#include "absl/strings/string_view.h"
+
+namespace grpc_core {
+
+class JsonArgs {
+ public:
+ JsonArgs() = default;
+ virtual ~JsonArgs() = default;
+
+ virtual bool IsEnabled(absl::string_view /*key*/) const { return true; }
+};
+
+} // namespace grpc_core
+
+#endif // GRPC_CORE_LIB_JSON_JSON_ARGS_H
diff --git a/src/core/lib/json/json_channel_args.h b/src/core/lib/json/json_channel_args.h
new file mode 100644
index 00000000000..668cb4aacdc
--- /dev/null
+++ b/src/core/lib/json/json_channel_args.h
@@ -0,0 +1,42 @@
+// Copyright 2022 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_CORE_LIB_JSON_JSON_CHANNEL_ARGS_H
+#define GRPC_CORE_LIB_JSON_JSON_CHANNEL_ARGS_H
+
+#include
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/json/json_args.h"
+
+namespace grpc_core {
+
+class JsonChannelArgs : public JsonArgs {
+ public:
+ explicit JsonChannelArgs(const ChannelArgs& args) : args_(args) {}
+
+ bool IsEnabled(absl::string_view key) const override {
+ return args_.GetBool(key).value_or(false);
+ }
+
+ private:
+ ChannelArgs args_;
+};
+
+} // namespace grpc_core
+
+#endif // GRPC_CORE_LIB_JSON_JSON_CHANNEL_ARGS_H
diff --git a/src/core/lib/json/json_object_loader.cc b/src/core/lib/json/json_object_loader.cc
new file mode 100644
index 00000000000..ae327101af1
--- /dev/null
+++ b/src/core/lib/json/json_object_loader.cc
@@ -0,0 +1,204 @@
+// 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.
+
+#include
+
+#include "src/core/lib/json/json_object_loader.h"
+
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
+#include "absl/strings/strip.h"
+
+namespace grpc_core {
+
+void ErrorList::PushField(absl::string_view ext) {
+ // Skip leading '.' for top-level field names.
+ if (fields_.empty()) absl::ConsumePrefix(&ext, ".");
+ fields_.emplace_back(std::string(ext));
+}
+
+void ErrorList::PopField() { fields_.pop_back(); }
+
+void ErrorList::AddError(absl::string_view error) {
+ field_errors_[absl::StrJoin(fields_, "")].emplace_back(error);
+}
+
+bool ErrorList::FieldHasErrors() const {
+ return field_errors_.find(absl::StrJoin(fields_, "")) != field_errors_.end();
+}
+
+absl::Status ErrorList::status() const {
+ if (field_errors_.empty()) return absl::OkStatus();
+ std::vector errors;
+ for (const auto& p : field_errors_) {
+ if (p.second.size() > 1) {
+ errors.emplace_back(absl::StrCat("field:", p.first, " errors:[",
+ absl::StrJoin(p.second, "; "), "]"));
+ } else {
+ errors.emplace_back(
+ absl::StrCat("field:", p.first, " error:", p.second[0]));
+ }
+ }
+ return absl::InvalidArgumentError(absl::StrCat(
+ "errors validating JSON: [", absl::StrJoin(errors, "; "), "]"));
+}
+
+namespace json_detail {
+
+void LoadScalar::LoadInto(const Json& json, const JsonArgs& /*args*/, void* dst,
+ ErrorList* errors) const {
+ // We accept either STRING or NUMBER for numeric values, as per
+ // https://developers.google.com/protocol-buffers/docs/proto3#json.
+ if (json.type() != Json::Type::STRING &&
+ (!IsNumber() || json.type() != Json::Type::NUMBER)) {
+ errors->AddError(
+ absl::StrCat("is not a ", IsNumber() ? "number" : "string"));
+ return;
+ }
+ return LoadInto(json.string_value(), dst, errors);
+}
+
+bool LoadString::IsNumber() const { return false; }
+
+void LoadString::LoadInto(const std::string& value, void* dst,
+ ErrorList*) const {
+ *static_cast(dst) = value;
+}
+
+bool LoadDuration::IsNumber() const { return false; }
+
+void LoadDuration::LoadInto(const std::string& value, void* dst,
+ ErrorList* errors) const {
+ absl::string_view buf(value);
+ if (!absl::ConsumeSuffix(&buf, "s")) {
+ errors->AddError("Not a duration (no s suffix)");
+ return;
+ }
+ buf = absl::StripAsciiWhitespace(buf);
+ auto decimal_point = buf.find('.');
+ int nanos = 0;
+ if (decimal_point != absl::string_view::npos) {
+ absl::string_view after_decimal = buf.substr(decimal_point + 1);
+ buf = buf.substr(0, decimal_point);
+ if (!absl::SimpleAtoi(after_decimal, &nanos)) {
+ errors->AddError("Not a duration (not a number of nanoseconds)");
+ return;
+ }
+ if (after_decimal.length() > 9) {
+ // We don't accept greater precision than nanos.
+ errors->AddError("Not a duration (too many digits after decimal)");
+ return;
+ }
+ for (size_t i = 0; i < (9 - after_decimal.length()); ++i) {
+ nanos *= 10;
+ }
+ }
+ int seconds;
+ if (!absl::SimpleAtoi(buf, &seconds)) {
+ errors->AddError("Not a duration (not a number of seconds)");
+ return;
+ }
+ *static_cast(dst) =
+ Duration::FromSecondsAndNanoseconds(seconds, nanos);
+}
+
+bool LoadNumber::IsNumber() const { return true; }
+
+void LoadBool::LoadInto(const Json& json, const JsonArgs&, void* dst,
+ ErrorList* errors) const {
+ if (json.type() == Json::Type::JSON_TRUE) {
+ *static_cast(dst) = true;
+ } else if (json.type() == Json::Type::JSON_FALSE) {
+ *static_cast(dst) = false;
+ } else {
+ errors->AddError("is not a boolean");
+ }
+}
+
+void LoadUnprocessedJsonObject::LoadInto(const Json& json, const JsonArgs&,
+ void* dst, ErrorList* errors) const {
+ if (json.type() != Json::Type::OBJECT) {
+ errors->AddError("is not an object");
+ return;
+ }
+ *static_cast(dst) = json.object_value();
+}
+
+void LoadVector::LoadInto(const Json& json, const JsonArgs& args, void* dst,
+ ErrorList* errors) const {
+ if (json.type() != Json::Type::ARRAY) {
+ errors->AddError("is not an array");
+ return;
+ }
+ const auto& array = json.array_value();
+ for (size_t i = 0; i < array.size(); ++i) {
+ ScopedField field(errors, absl::StrCat("[", i, "]"));
+ LoadOne(array[i], args, dst, errors);
+ }
+}
+
+void LoadMap::LoadInto(const Json& json, const JsonArgs& args, void* dst,
+ ErrorList* errors) const {
+ if (json.type() != Json::Type::OBJECT) {
+ errors->AddError("is not an object");
+ return;
+ }
+ for (const auto& pair : json.object_value()) {
+ ScopedField field(errors, absl::StrCat("[\"", pair.first, "\"]"));
+ LoadOne(pair.second, args, pair.first, dst, errors);
+ }
+}
+
+bool LoadObject(const Json& json, const JsonArgs& args, const Element* elements,
+ size_t num_elements, void* dst, ErrorList* errors) {
+ if (json.type() != Json::Type::OBJECT) {
+ errors->AddError("is not an object");
+ return false;
+ }
+ for (size_t i = 0; i < num_elements; ++i) {
+ const Element& element = elements[i];
+ if (element.enable_key != nullptr && !args.IsEnabled(element.enable_key)) {
+ continue;
+ }
+ ScopedField field(errors, absl::StrCat(".", element.name));
+ const auto& it = json.object_value().find(element.name);
+ if (it == json.object_value().end()) {
+ if (element.optional) continue;
+ errors->AddError("field not present");
+ continue;
+ }
+ char* field_dst = static_cast(dst) + element.member_offset;
+ element.loader->LoadInto(it->second, args, field_dst, errors);
+ }
+ return true;
+}
+
+const Json* GetJsonObjectField(const Json::Object& json,
+ absl::string_view field, ErrorList* errors,
+ bool required) {
+ auto it = json.find(std::string(field));
+ if (it == json.end()) {
+ if (required) errors->AddError("field not present");
+ return nullptr;
+ }
+ return &it->second;
+}
+
+} // namespace json_detail
+} // namespace grpc_core
diff --git a/src/core/lib/json/json_object_loader.h b/src/core/lib/json/json_object_loader.h
new file mode 100644
index 00000000000..6ccdc2ebcca
--- /dev/null
+++ b/src/core/lib/json/json_object_loader.h
@@ -0,0 +1,544 @@
+// 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_CORE_LIB_JSON_JSON_OBJECT_LOADER_H
+#define GRPC_CORE_LIB_JSON_JSON_OBJECT_LOADER_H
+
+#include
+
+#include
+#include
+#include