1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include <jsonpb/verify.h>
18*288bf522SAndroid Build Coastguard Worker
19*288bf522SAndroid Build Coastguard Worker #include <iostream>
20*288bf522SAndroid Build Coastguard Worker #include <memory>
21*288bf522SAndroid Build Coastguard Worker #include <sstream>
22*288bf522SAndroid Build Coastguard Worker #include <string>
23*288bf522SAndroid Build Coastguard Worker
24*288bf522SAndroid Build Coastguard Worker #include <android-base/strings.h>
25*288bf522SAndroid Build Coastguard Worker #include <google/protobuf/descriptor.h>
26*288bf522SAndroid Build Coastguard Worker #include <google/protobuf/descriptor.pb.h>
27*288bf522SAndroid Build Coastguard Worker #include <google/protobuf/message.h>
28*288bf522SAndroid Build Coastguard Worker #include <google/protobuf/reflection.h>
29*288bf522SAndroid Build Coastguard Worker #include <json/reader.h>
30*288bf522SAndroid Build Coastguard Worker #include <json/writer.h>
31*288bf522SAndroid Build Coastguard Worker #include <jsonpb/jsonpb.h>
32*288bf522SAndroid Build Coastguard Worker
33*288bf522SAndroid Build Coastguard Worker namespace android {
34*288bf522SAndroid Build Coastguard Worker namespace jsonpb {
35*288bf522SAndroid Build Coastguard Worker
36*288bf522SAndroid Build Coastguard Worker using google::protobuf::FieldDescriptor;
37*288bf522SAndroid Build Coastguard Worker using google::protobuf::FieldDescriptorProto;
38*288bf522SAndroid Build Coastguard Worker using google::protobuf::Message;
39*288bf522SAndroid Build Coastguard Worker
40*288bf522SAndroid Build Coastguard Worker // Return json_name of the field. If it is not set, return the name of the
41*288bf522SAndroid Build Coastguard Worker // field.
GetJsonName(const FieldDescriptor & field_descriptor)42*288bf522SAndroid Build Coastguard Worker const std::string& GetJsonName(const FieldDescriptor& field_descriptor) {
43*288bf522SAndroid Build Coastguard Worker // The current version of libprotobuf does not define
44*288bf522SAndroid Build Coastguard Worker // FieldDescriptor::has_json_name() yet. Use a workaround.
45*288bf522SAndroid Build Coastguard Worker // TODO: use field_descriptor.has_json_name() when libprotobuf version is
46*288bf522SAndroid Build Coastguard Worker // bumped.
47*288bf522SAndroid Build Coastguard Worker FieldDescriptorProto proto;
48*288bf522SAndroid Build Coastguard Worker field_descriptor.CopyTo(&proto);
49*288bf522SAndroid Build Coastguard Worker return proto.has_json_name() ? field_descriptor.json_name() : field_descriptor.name();
50*288bf522SAndroid Build Coastguard Worker }
51*288bf522SAndroid Build Coastguard Worker
AllFieldsAreKnown(const Message & message,const Json::Value & json,std::vector<std::string> * path,std::stringstream * error)52*288bf522SAndroid Build Coastguard Worker bool AllFieldsAreKnown(const Message& message, const Json::Value& json,
53*288bf522SAndroid Build Coastguard Worker std::vector<std::string>* path, std::stringstream* error) {
54*288bf522SAndroid Build Coastguard Worker if (!json.isObject()) {
55*288bf522SAndroid Build Coastguard Worker *error << base::Join(*path, ".") << ": Not a JSON object\n";
56*288bf522SAndroid Build Coastguard Worker return false;
57*288bf522SAndroid Build Coastguard Worker }
58*288bf522SAndroid Build Coastguard Worker auto&& descriptor = message.GetDescriptor();
59*288bf522SAndroid Build Coastguard Worker
60*288bf522SAndroid Build Coastguard Worker auto json_members = json.getMemberNames();
61*288bf522SAndroid Build Coastguard Worker std::set<std::string> json_keys{json_members.begin(), json_members.end()};
62*288bf522SAndroid Build Coastguard Worker
63*288bf522SAndroid Build Coastguard Worker std::set<std::string> known_keys;
64*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < descriptor->field_count(); ++i) {
65*288bf522SAndroid Build Coastguard Worker known_keys.insert(GetJsonName(*descriptor->field(i)));
66*288bf522SAndroid Build Coastguard Worker }
67*288bf522SAndroid Build Coastguard Worker
68*288bf522SAndroid Build Coastguard Worker std::set<std::string> unknown_keys;
69*288bf522SAndroid Build Coastguard Worker std::set_difference(json_keys.begin(), json_keys.end(), known_keys.begin(), known_keys.end(),
70*288bf522SAndroid Build Coastguard Worker std::inserter(unknown_keys, unknown_keys.begin()));
71*288bf522SAndroid Build Coastguard Worker
72*288bf522SAndroid Build Coastguard Worker if (!unknown_keys.empty()) {
73*288bf522SAndroid Build Coastguard Worker *error << base::Join(*path, ".") << ": contains unknown keys: ["
74*288bf522SAndroid Build Coastguard Worker << base::Join(unknown_keys, ", ") << "]. Keys must be a known field name of "
75*288bf522SAndroid Build Coastguard Worker << descriptor->full_name() << "(or its json_name option if set): ["
76*288bf522SAndroid Build Coastguard Worker << base::Join(known_keys, ", ") << "]\n";
77*288bf522SAndroid Build Coastguard Worker return false;
78*288bf522SAndroid Build Coastguard Worker }
79*288bf522SAndroid Build Coastguard Worker
80*288bf522SAndroid Build Coastguard Worker bool success = true;
81*288bf522SAndroid Build Coastguard Worker
82*288bf522SAndroid Build Coastguard Worker // Check message fields.
83*288bf522SAndroid Build Coastguard Worker auto&& reflection = message.GetReflection();
84*288bf522SAndroid Build Coastguard Worker std::vector<const FieldDescriptor*> set_field_descriptors;
85*288bf522SAndroid Build Coastguard Worker reflection->ListFields(message, &set_field_descriptors);
86*288bf522SAndroid Build Coastguard Worker for (auto&& field_descriptor : set_field_descriptors) {
87*288bf522SAndroid Build Coastguard Worker if (field_descriptor->cpp_type() != FieldDescriptor::CppType::CPPTYPE_MESSAGE) {
88*288bf522SAndroid Build Coastguard Worker continue;
89*288bf522SAndroid Build Coastguard Worker }
90*288bf522SAndroid Build Coastguard Worker if (field_descriptor->is_map()) {
91*288bf522SAndroid Build Coastguard Worker continue;
92*288bf522SAndroid Build Coastguard Worker }
93*288bf522SAndroid Build Coastguard Worker
94*288bf522SAndroid Build Coastguard Worker const std::string& json_name = GetJsonName(*field_descriptor);
95*288bf522SAndroid Build Coastguard Worker const Json::Value& json_value = json[json_name];
96*288bf522SAndroid Build Coastguard Worker
97*288bf522SAndroid Build Coastguard Worker if (field_descriptor->is_repeated()) {
98*288bf522SAndroid Build Coastguard Worker auto&& fields = reflection->GetRepeatedFieldRef<Message>(message, field_descriptor);
99*288bf522SAndroid Build Coastguard Worker
100*288bf522SAndroid Build Coastguard Worker if (json_value.type() != Json::ValueType::arrayValue) {
101*288bf522SAndroid Build Coastguard Worker *error << base::Join(*path, ".") << ": not a JSON list. This should not happen.\n";
102*288bf522SAndroid Build Coastguard Worker success = false;
103*288bf522SAndroid Build Coastguard Worker continue;
104*288bf522SAndroid Build Coastguard Worker }
105*288bf522SAndroid Build Coastguard Worker
106*288bf522SAndroid Build Coastguard Worker if (json_value.size() != static_cast<size_t>(fields.size())) {
107*288bf522SAndroid Build Coastguard Worker *error << base::Join(*path, ".") << ": JSON list has size " << json_value.size()
108*288bf522SAndroid Build Coastguard Worker << " but message has size " << fields.size() << ". This should not happen.\n";
109*288bf522SAndroid Build Coastguard Worker success = false;
110*288bf522SAndroid Build Coastguard Worker continue;
111*288bf522SAndroid Build Coastguard Worker }
112*288bf522SAndroid Build Coastguard Worker
113*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Message> scratch_space(fields.NewMessage());
114*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < fields.size(); ++i) {
115*288bf522SAndroid Build Coastguard Worker path->push_back(json_name + "[" + std::to_string(i) + "]");
116*288bf522SAndroid Build Coastguard Worker auto res =
117*288bf522SAndroid Build Coastguard Worker AllFieldsAreKnown(fields.Get(i, scratch_space.get()), json_value[i], path, error);
118*288bf522SAndroid Build Coastguard Worker path->pop_back();
119*288bf522SAndroid Build Coastguard Worker if (!res) {
120*288bf522SAndroid Build Coastguard Worker success = false;
121*288bf522SAndroid Build Coastguard Worker }
122*288bf522SAndroid Build Coastguard Worker }
123*288bf522SAndroid Build Coastguard Worker } else {
124*288bf522SAndroid Build Coastguard Worker auto&& field = reflection->GetMessage(message, field_descriptor);
125*288bf522SAndroid Build Coastguard Worker path->push_back(json_name);
126*288bf522SAndroid Build Coastguard Worker auto res = AllFieldsAreKnown(field, json_value, path, error);
127*288bf522SAndroid Build Coastguard Worker path->pop_back();
128*288bf522SAndroid Build Coastguard Worker if (!res) {
129*288bf522SAndroid Build Coastguard Worker success = false;
130*288bf522SAndroid Build Coastguard Worker }
131*288bf522SAndroid Build Coastguard Worker }
132*288bf522SAndroid Build Coastguard Worker }
133*288bf522SAndroid Build Coastguard Worker return success;
134*288bf522SAndroid Build Coastguard Worker }
135*288bf522SAndroid Build Coastguard Worker
AllFieldsAreKnown(const google::protobuf::Message & message,const std::string & json,std::string * error)136*288bf522SAndroid Build Coastguard Worker bool AllFieldsAreKnown(const google::protobuf::Message& message, const std::string& json,
137*288bf522SAndroid Build Coastguard Worker std::string* error) {
138*288bf522SAndroid Build Coastguard Worker Json::CharReaderBuilder builder;
139*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
140*288bf522SAndroid Build Coastguard Worker Json::Value value;
141*288bf522SAndroid Build Coastguard Worker if (!reader->parse(&*json.begin(), &*json.end(), &value, error)) {
142*288bf522SAndroid Build Coastguard Worker return false;
143*288bf522SAndroid Build Coastguard Worker }
144*288bf522SAndroid Build Coastguard Worker
145*288bf522SAndroid Build Coastguard Worker std::stringstream errorss;
146*288bf522SAndroid Build Coastguard Worker std::vector<std::string> json_tree_path{"<root>"};
147*288bf522SAndroid Build Coastguard Worker if (!AllFieldsAreKnown(message, value, &json_tree_path, &errorss)) {
148*288bf522SAndroid Build Coastguard Worker *error = errorss.str();
149*288bf522SAndroid Build Coastguard Worker return false;
150*288bf522SAndroid Build Coastguard Worker }
151*288bf522SAndroid Build Coastguard Worker return true;
152*288bf522SAndroid Build Coastguard Worker }
153*288bf522SAndroid Build Coastguard Worker
EqReformattedJson(const std::string & json,google::protobuf::Message * scratch_space,std::string * error)154*288bf522SAndroid Build Coastguard Worker bool EqReformattedJson(const std::string& json, google::protobuf::Message* scratch_space,
155*288bf522SAndroid Build Coastguard Worker std::string* error) {
156*288bf522SAndroid Build Coastguard Worker Json::CharReaderBuilder builder;
157*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
158*288bf522SAndroid Build Coastguard Worker Json::Value old_json;
159*288bf522SAndroid Build Coastguard Worker if (!reader->parse(&*json.begin(), &*json.end(), &old_json, error)) {
160*288bf522SAndroid Build Coastguard Worker return false;
161*288bf522SAndroid Build Coastguard Worker }
162*288bf522SAndroid Build Coastguard Worker
163*288bf522SAndroid Build Coastguard Worker auto new_json_string = internal::FormatJson(json, scratch_space);
164*288bf522SAndroid Build Coastguard Worker if (!new_json_string.ok()) {
165*288bf522SAndroid Build Coastguard Worker *error = new_json_string.error();
166*288bf522SAndroid Build Coastguard Worker return false;
167*288bf522SAndroid Build Coastguard Worker }
168*288bf522SAndroid Build Coastguard Worker Json::Value new_json;
169*288bf522SAndroid Build Coastguard Worker if (!reader->parse(&*new_json_string->begin(), &*new_json_string->end(), &new_json, error)) {
170*288bf522SAndroid Build Coastguard Worker return false;
171*288bf522SAndroid Build Coastguard Worker }
172*288bf522SAndroid Build Coastguard Worker
173*288bf522SAndroid Build Coastguard Worker if (old_json != new_json) {
174*288bf522SAndroid Build Coastguard Worker std::stringstream ss;
175*288bf522SAndroid Build Coastguard Worker ss << "Formatted JSON tree does not match source. Possible reasons "
176*288bf522SAndroid Build Coastguard Worker "include: \n"
177*288bf522SAndroid Build Coastguard Worker "- JSON Integers (without quotes) are matched against 64-bit "
178*288bf522SAndroid Build Coastguard Worker "integers in Prototype\n"
179*288bf522SAndroid Build Coastguard Worker " (Reformatted integers will now have quotes.) Quote these integers "
180*288bf522SAndroid Build Coastguard Worker "in source\n"
181*288bf522SAndroid Build Coastguard Worker " JSON or use 32-bit integers instead.\n"
182*288bf522SAndroid Build Coastguard Worker "- Enum values are stored as integers in source JSON file. Use enum "
183*288bf522SAndroid Build Coastguard Worker "value name \n"
184*288bf522SAndroid Build Coastguard Worker " string instead, or change schema field to string / integers.\n"
185*288bf522SAndroid Build Coastguard Worker "- JSON keys are re-formatted to be lowerCamelCase. To fix, define "
186*288bf522SAndroid Build Coastguard Worker "json_name "
187*288bf522SAndroid Build Coastguard Worker "option\n"
188*288bf522SAndroid Build Coastguard Worker " for appropriate fields.\n"
189*288bf522SAndroid Build Coastguard Worker "\n"
190*288bf522SAndroid Build Coastguard Worker "Reformatted JSON is printed below.\n";
191*288bf522SAndroid Build Coastguard Worker Json::StreamWriterBuilder factory;
192*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
193*288bf522SAndroid Build Coastguard Worker writer->write(new_json, &ss);
194*288bf522SAndroid Build Coastguard Worker *error = ss.str();
195*288bf522SAndroid Build Coastguard Worker return false;
196*288bf522SAndroid Build Coastguard Worker }
197*288bf522SAndroid Build Coastguard Worker return true;
198*288bf522SAndroid Build Coastguard Worker }
199*288bf522SAndroid Build Coastguard Worker
200*288bf522SAndroid Build Coastguard Worker namespace internal {
FormatJson(const std::string & json,google::protobuf::Message * scratch_space)201*288bf522SAndroid Build Coastguard Worker ErrorOr<std::string> FormatJson(const std::string& json, google::protobuf::Message* scratch_space) {
202*288bf522SAndroid Build Coastguard Worker auto res = internal::JsonStringToMessage(json, scratch_space);
203*288bf522SAndroid Build Coastguard Worker if (!res.ok()) {
204*288bf522SAndroid Build Coastguard Worker return MakeError<std::string>(res.error());
205*288bf522SAndroid Build Coastguard Worker }
206*288bf522SAndroid Build Coastguard Worker return MessageToJsonString(*scratch_space);
207*288bf522SAndroid Build Coastguard Worker }
208*288bf522SAndroid Build Coastguard Worker } // namespace internal
209*288bf522SAndroid Build Coastguard Worker
210*288bf522SAndroid Build Coastguard Worker } // namespace jsonpb
211*288bf522SAndroid Build Coastguard Worker } // namespace android
212