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_LIB_JSON_JSON_UTIL_H
20 #define GRPC_SRC_CORE_LIB_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
35 #include "src/core/lib/gprpp/time.h"
36 #include "src/core/lib/iomgr/error.h"
37 #include "src/core/lib/json/json.h"
38
39 namespace grpc_core {
40
41 // Parses a JSON field of the form generated for a google.proto.Duration
42 // proto message, as per:
43 // https://developers.google.com/protocol-buffers/docs/proto3#json
44 // Returns true on success, false otherwise.
45 bool ParseDurationFromJson(const Json& field, Duration* duration);
46
47 //
48 // Helper functions for extracting types from JSON.
49 // Return true on success, false otherwise. If an error is encountered during
50 // parsing, a descriptive error is appended to \a error_list.
51 //
52 template <typename NumericType>
ExtractJsonNumber(const Json & json,absl::string_view field_name,NumericType * output,std::vector<grpc_error_handle> * error_list)53 bool ExtractJsonNumber(const Json& json, absl::string_view field_name,
54 NumericType* output,
55 std::vector<grpc_error_handle>* error_list) {
56 static_assert(std::is_integral<NumericType>::value, "Integral required");
57 if (json.type() != Json::Type::kNumber &&
58 json.type() != Json::Type::kString) {
59 error_list->push_back(GRPC_ERROR_CREATE(absl::StrCat(
60 "field:", field_name, " error:type should be NUMBER or STRING")));
61 return false;
62 }
63 if (!absl::SimpleAtoi(json.string(), output)) {
64 error_list->push_back(GRPC_ERROR_CREATE(
65 absl::StrCat("field:", field_name, " error:failed to parse.")));
66 return false;
67 }
68 return true;
69 }
70
71 bool ExtractJsonBool(const Json& json, absl::string_view field_name,
72 bool* output, std::vector<grpc_error_handle>* error_list);
73
74 // OutputType can be std::string or absl::string_view.
75 template <typename OutputType>
ExtractJsonString(const Json & json,absl::string_view field_name,OutputType * output,std::vector<grpc_error_handle> * error_list)76 bool ExtractJsonString(const Json& json, absl::string_view field_name,
77 OutputType* output,
78 std::vector<grpc_error_handle>* error_list) {
79 if (json.type() != Json::Type::kString) {
80 *output = "";
81 error_list->push_back(GRPC_ERROR_CREATE(
82 absl::StrCat("field:", field_name, " error:type should be STRING")));
83 return false;
84 }
85 *output = json.string();
86 return true;
87 }
88
89 bool ExtractJsonArray(const Json& json, absl::string_view field_name,
90 const Json::Array** output,
91 std::vector<grpc_error_handle>* error_list);
92
93 bool ExtractJsonObject(const Json& json, absl::string_view field_name,
94 const Json::Object** output,
95 std::vector<grpc_error_handle>* error_list);
96
97 // Wrappers for automatically choosing one of the above functions based
98 // on output parameter type.
99 template <typename NumericType>
ExtractJsonType(const Json & json,absl::string_view field_name,NumericType * output,std::vector<grpc_error_handle> * error_list)100 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
101 NumericType* output,
102 std::vector<grpc_error_handle>* error_list) {
103 return ExtractJsonNumber(json, field_name, output, error_list);
104 }
ExtractJsonType(const Json & json,absl::string_view field_name,bool * output,std::vector<grpc_error_handle> * error_list)105 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
106 bool* output,
107 std::vector<grpc_error_handle>* error_list) {
108 return ExtractJsonBool(json, field_name, output, error_list);
109 }
ExtractJsonType(const Json & json,absl::string_view field_name,std::string * output,std::vector<grpc_error_handle> * error_list)110 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
111 std::string* output,
112 std::vector<grpc_error_handle>* error_list) {
113 return ExtractJsonString(json, field_name, output, error_list);
114 }
ExtractJsonType(const Json & json,absl::string_view field_name,absl::string_view * output,std::vector<grpc_error_handle> * error_list)115 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
116 absl::string_view* output,
117 std::vector<grpc_error_handle>* error_list) {
118 return ExtractJsonString(json, field_name, output, error_list);
119 }
ExtractJsonType(const Json & json,absl::string_view field_name,const Json::Array ** output,std::vector<grpc_error_handle> * error_list)120 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
121 const Json::Array** output,
122 std::vector<grpc_error_handle>* error_list) {
123 return ExtractJsonArray(json, field_name, output, error_list);
124 }
ExtractJsonType(const Json & json,absl::string_view field_name,const Json::Object ** output,std::vector<grpc_error_handle> * error_list)125 inline bool ExtractJsonType(const Json& json, absl::string_view field_name,
126 const Json::Object** output,
127 std::vector<grpc_error_handle>* error_list) {
128 return ExtractJsonObject(json, field_name, output, error_list);
129 }
130
131 // Extracts a field from a JSON object, automatically selecting the type
132 // of parsing based on the output parameter type.
133 // If the field is not present, returns false, and if required is true,
134 // adds an error to error_list.
135 // Upon any other error, adds an error to error_list and returns false.
136 template <typename T>
137 bool ParseJsonObjectField(const Json::Object& object,
138 absl::string_view field_name, T* output,
139 std::vector<grpc_error_handle>* error_list,
140 bool required = true) {
141 // TODO(roth): Once we can use C++14 heterogenous lookups, stop
142 // creating a std::string here.
143 auto it = object.find(std::string(field_name));
144 if (it == object.end()) {
145 if (required) {
146 error_list->push_back(GRPC_ERROR_CREATE(
147 absl::StrCat("field:", field_name, " error:does not exist.")));
148 }
149 return false;
150 }
151 auto& child_object_json = it->second;
152 return ExtractJsonType(child_object_json, field_name, output, error_list);
153 }
154
155 // Alternative to ParseJsonObjectField() for duration-value fields.
156 bool ParseJsonObjectFieldAsDuration(const Json::Object& object,
157 absl::string_view field_name,
158 Duration* output,
159 std::vector<grpc_error_handle>* error_list,
160 bool required = true);
161
162 } // namespace grpc_core
163
164 #endif // GRPC_SRC_CORE_LIB_JSON_JSON_UTIL_H
165