1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 #pragma once 19 20 #include <sstream> 21 #include <string> 22 #include <vector> 23 24 #include <google/protobuf/message.h> 25 #include <json/reader.h> 26 #include <json/value.h> 27 #include <jsonpb/jsonpb.h> 28 29 namespace android { 30 namespace jsonpb { 31 32 // Ensure that the JSON file has no unknown fields that is not defined in proto. 33 // Because we want forwards compatibility, the parser of JSON files must ignore 34 // unknown fields. This is achievable with libprotobuf version > 3.0-beta. 35 // - <= 3.0-beta: we have to check unknown fields manually, and parser cannot 36 // use libprotobuf 37 // to parse JSON files. 38 // - < 3.5: libprotobuf discards all unknown fields. We can still check unknown 39 // fields manually, but 40 // an easier way to check is `json == FormatJson(json)` (schematically) 41 // - >= 3.5: Unknown fields are preserved, so FormatJson() may contain these 42 // unknown fields. We can 43 // still check fields manually, or use reflection mechanism. 44 // 45 // For example, if a new field "foo" is added to cgroups.json but not to 46 // cgroups.proto, libprocessgroup could technically read the value of "foo" by 47 // using other libraries that parse JSON strings, effectively working around the 48 // schema. 49 // 50 // This test also ensures that the parser does not use alternative key names. 51 // For example, if the proto file states: message Foo { string foo_bar = 1; 52 // string bar_baz = 2 [json_name = "BarBaz"]; } Then the parser accepts 53 // "foo_bar" "fooBar", "bar_baz", "BarBaz" as valid key names. Here, we enforce 54 // that the JSON file must use "foo_bar" and "BarBaz". 55 // 56 // Requiring this avoids surprises like: 57 // message Foo { string FooBar = 1; } 58 // { "fooBar" : "s" } 59 // conforms with the schema, because libprotobuf accept "fooBar" as a valid key. 60 // The correct schema should be: 61 // message Foo { string foo_bar = 1 [json_name="fooBar"]; } 62 // 63 // Params: 64 // path: path to navigate inside JSON tree. For example, {"foo", "bar"} for 65 // the value "string" in 66 // {"foo": {"bar" : "string"}} 67 bool AllFieldsAreKnown(const google::protobuf::Message& message, 68 const std::string& json, std::string* error); 69 70 // Format the given JSON string according to Prototype T. This will serialize 71 // the JSON string to a Prototype message, then re-print the message as JSON. By 72 // reformatting the JSON string, we effectively enforces that the JSON source 73 // file uses conventions of Protobuf's JSON writer; e.g. 64-bit integers / 74 // special floating point numbers (inf, NaN, etc.) in strings, enum values in 75 // names, etc. 76 // 77 // Params: 78 // scratch_space: The scratch space to use to store the Protobuf message. It 79 // must be a pointer 80 // to the schema that the JSON string conforms to. 81 bool EqReformattedJson(const std::string& json, 82 google::protobuf::Message* scratch_space, 83 std::string* error); 84 85 namespace internal { 86 // See EqReformattedJson(). 87 ErrorOr<std::string> FormatJson(const std::string& json, 88 google::protobuf::Message* scratch_space); 89 90 } // namespace internal 91 92 } // namespace jsonpb 93 } // namespace android 94