1 //
2 //
3 // Copyright 2020 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #ifndef GRPC_SRC_CORE_UTIL_JSON_JSON_UTIL_H
20 #define GRPC_SRC_CORE_UTIL_JSON_JSON_UTIL_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <algorithm>
25 #include <map>
26 #include <string>
27 #include <type_traits>
28 #include <utility>
29 #include <vector>
30
31 #include "absl/strings/numbers.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/string_view.h"
34 #include "src/core/lib/iomgr/error.h"
35 #include "src/core/util/json/json.h"
36 #include "src/core/util/time.h"
37
38 namespace grpc_core {
39
40 // Parses a JSON field of the form generated for a google.proto.Duration
41 // proto message, as per:
42 // https://developers.google.com/protocol-buffers/docs/proto3#json
43 // Returns true on success, false otherwise.
44 bool ParseDurationFromJson(const Json& field, Duration* duration);
45
46 //
47 // Helper functions for extracting types from JSON.
48 // Return true on success, false otherwise. If an error is encountered during
49 // parsing, a descriptive error is appended to \a error_list.
50 //
51 template <typename NumericType>
ExtractJsonNumber(const Json & json,absl::string_view field_name,NumericType * output,std::vector<grpc_error_handle> * error_list)52 bool ExtractJsonNumber(const Json& json, absl::string_view field_name,
53 NumericType* output,
54 std::vector<grpc_error_handle>* error_list) {
55 static_assert(std::is_integral<NumericType>::value, "Integral required");
56 if (json.type() != Json::Type::kNumber &&
57 json.type() != Json::Type::kString) {
58 error_list->push_back(GRPC_ERROR_CREATE(absl::StrCat(
59 "field:", field_name, " error:type should be NUMBER or STRING")));
60 return false;
61 }
62 if (!absl::SimpleAtoi(json.string(), output)) {
63 error_list->push_back(GRPC_ERROR_CREATE(
64 absl::StrCat("field:", field_name, " error:failed to parse.")));
65 return false;
66 }
67 return true;
68 }
69
70 bool ExtractJsonBool(const Json& json, absl::string_view field_name,
71 bool* output, std::vector<grpc_error_handle>* error_list);
72
73 // OutputType can be std::string or absl::string_view.
74 template <typename OutputType>
ExtractJsonString(const Json & json,absl::string_view field_name,OutputType * output,std::vector<grpc_error_handle> * error_list)75 bool ExtractJsonString(const Json& json, absl::string_view field_name,
76 OutputType* output,
77 std::vector<grpc_error_handle>* error_list) {
78 if (json.type() != Json::Type::kString) {
79 *output = "";
80 error_list->push_back(GRPC_ERROR_CREATE(
81 absl::StrCat("field:", field_name, " error:type should be STRING")));
82 return false;
83 }
84 *output = json.string();
85 return true;
86 }
87
88 bool ExtractJsonArray(const Json& json, absl::string_view field_name,
89 const Json::Array** output,
90 std::vector<grpc_error_handle>* error_list);
91
92 bool ExtractJsonObject(const Json& json, absl::string_view field_name,
93 const Json::Object** output,
94 std::vector<grpc_error_handle>* error_list);
95
96 // Wrappers for automatically choosing one of the above functions based
97 // on output parameter type.
98 template <typename NumericType>
ExtractJsonType(const Json & json,absl::string_view field_name,NumericType * output,std::vector<grpc_error_handle> * error_list)99 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
100 NumericType* output,
101 std::vector<grpc_error_handle>* error_list) {
102 return ExtractJsonNumber(json, field_name, output, error_list);
103 }
ExtractJsonType(const Json & json,absl::string_view field_name,bool * output,std::vector<grpc_error_handle> * error_list)104 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
105 bool* output,
106 std::vector<grpc_error_handle>* error_list) {
107 return ExtractJsonBool(json, field_name, output, error_list);
108 }
ExtractJsonType(const Json & json,absl::string_view field_name,std::string * output,std::vector<grpc_error_handle> * error_list)109 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
110 std::string* output,
111 std::vector<grpc_error_handle>* error_list) {
112 return ExtractJsonString(json, field_name, output, error_list);
113 }
ExtractJsonType(const Json & json,absl::string_view field_name,absl::string_view * output,std::vector<grpc_error_handle> * error_list)114 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
115 absl::string_view* output,
116 std::vector<grpc_error_handle>* error_list) {
117 return ExtractJsonString(json, field_name, output, error_list);
118 }
ExtractJsonType(const Json & json,absl::string_view field_name,const Json::Array ** output,std::vector<grpc_error_handle> * error_list)119 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
120 const Json::Array** output,
121 std::vector<grpc_error_handle>* error_list) {
122 return ExtractJsonArray(json, field_name, output, error_list);
123 }
ExtractJsonType(const Json & json,absl::string_view field_name,const Json::Object ** output,std::vector<grpc_error_handle> * error_list)124 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
125 const Json::Object** output,
126 std::vector<grpc_error_handle>* error_list) {
127 return ExtractJsonObject(json, field_name, output, error_list);
128 }
129
130 // Extracts a field from a JSON object, automatically selecting the type
131 // of parsing based on the output parameter type.
132 // If the field is not present, returns false, and if required is true,
133 // adds an error to error_list.
134 // Upon any other error, adds an error to error_list and returns false.
135 template <typename T>
136 bool ParseJsonObjectField(const Json::Object& object,
137 absl::string_view field_name, T* output,
138 std::vector<grpc_error_handle>* error_list,
139 bool required = true) {
140 // TODO(roth): Once we can use C++14 heterogenous lookups, stop
141 // creating a std::string here.
142 auto it = object.find(std::string(field_name));
143 if (it == object.end()) {
144 if (required) {
145 error_list->push_back(GRPC_ERROR_CREATE(
146 absl::StrCat("field:", field_name, " error:does not exist.")));
147 }
148 return false;
149 }
150 auto& child_object_json = it->second;
151 return ExtractJsonType(child_object_json, field_name, output, error_list);
152 }
153
154 // Alternative to ParseJsonObjectField() for duration-value fields.
155 bool ParseJsonObjectFieldAsDuration(const Json::Object& object,
156 absl::string_view field_name,
157 Duration* output,
158 std::vector<grpc_error_handle>* error_list,
159 bool required = true);
160
161 } // namespace grpc_core
162
163 #endif // GRPC_SRC_CORE_UTIL_JSON_JSON_UTIL_H
164