kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/common/json_proto.cc (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 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 #include "kythe/cxx/common/json_proto.h" 18 19 #include <memory> 20 #include <string> 21 22 #include "absl/log/check.h" 23 #include "absl/log/log.h" 24 #include "absl/status/status.h" 25 #include "absl/status/statusor.h" 26 #include "absl/strings/str_cat.h" 27 #include "absl/strings/string_view.h" 28 #include "google/protobuf/any.pb.h" 29 #include "google/protobuf/io/coded_stream.h" 30 #include "google/protobuf/io/zero_copy_stream_impl_lite.h" 31 #include "google/protobuf/message.h" 32 #include "google/protobuf/type.pb.h" 33 #include "google/protobuf/util/json_util.h" 34 #include "google/protobuf/util/type_resolver.h" 35 #include "google/protobuf/util/type_resolver_util.h" 36 37 namespace kythe { 38 namespace { 39 using ::google::protobuf::DescriptorPool; 40 using ::google::protobuf::util::JsonParseOptions; 41 using ::google::protobuf::util::TypeResolver; 42 43 class PermissiveTypeResolver : public TypeResolver { 44 public: 45 explicit PermissiveTypeResolver(const DescriptorPool* pool) 46 : impl_(google::protobuf::util::NewTypeResolverForDescriptorPool("", 47 pool)) {} 48 49 absl::Status ResolveMessageType( 50 const std::string& type_url, 51 google::protobuf::Type* message_type) override { 52 absl::string_view adjusted = type_url; 53 adjusted.remove_prefix(type_url.rfind('/') + 1); 54 return impl_->ResolveMessageType(absl::StrCat("/", adjusted), message_type); 55 } 56 57 absl::Status ResolveEnumType(const std::string& type_url, 58 google::protobuf::Enum* enum_type) override { 59 absl::string_view adjusted = type_url; 60 adjusted.remove_prefix(type_url.rfind('/') + 1); 61 return impl_->ResolveEnumType(absl::StrCat("/", adjusted), enum_type); 62 } 63 64 private: 65 std::unique_ptr<TypeResolver> impl_; 66 }; 67 68 TypeResolver* GetGeneratedTypeResolver() { 69 static TypeResolver* generated_resolver = 70 new PermissiveTypeResolver(DescriptorPool::generated_pool()); 71 return generated_resolver; 72 } 73 74 struct MaybeDeleteResolver { 75 void operator()(TypeResolver* resolver) const { 76 if (resolver != GetGeneratedTypeResolver()) { 77 delete resolver; 78 } 79 } 80 }; 81 82 std::unique_ptr<TypeResolver, MaybeDeleteResolver> MakeTypeResolverForPool( 83 const DescriptorPool* pool) { 84 if (pool == DescriptorPool::generated_pool()) { 85 return std::unique_ptr<TypeResolver, MaybeDeleteResolver>( 86 GetGeneratedTypeResolver()); 87 } 88 return std::unique_ptr<TypeResolver, MaybeDeleteResolver>( 89 new PermissiveTypeResolver(pool)); 90 } 91 92 absl::Status WriteMessageAsJsonToStringInternal( 93 const google::protobuf::Message& message, std::string* out) { 94 auto resolver = 95 MakeTypeResolverForPool(message.GetDescriptor()->file()->pool()); 96 97 google::protobuf::util::JsonPrintOptions options; 98 options.preserve_proto_field_names = true; 99 100 auto status = google::protobuf::util::BinaryToJsonString( 101 resolver.get(), message.GetDescriptor()->full_name(), 102 message.SerializeAsString(), out, options); 103 if (!status.ok()) { 104 return absl::Status(static_cast<absl::StatusCode>(status.code()), 105 std::string(status.message())); 106 } 107 return absl::OkStatus(); 108 } 109 110 JsonParseOptions DefaultParseOptions() { 111 JsonParseOptions options; 112 options.case_insensitive_enum_parsing = false; 113 return options; 114 } 115 116 } // namespace 117 118 bool WriteMessageAsJsonToString(const google::protobuf::Message& message, 119 std::string* out) { 120 auto status = WriteMessageAsJsonToStringInternal(message, out); 121 if (!status.ok()) { 122 LOG(ERROR) << status.ToString(); 123 } 124 return status.ok(); 125 } 126 127 absl::StatusOr<std::string> WriteMessageAsJsonToString( 128 const google::protobuf::Message& message) { 129 std::string result; 130 auto status = WriteMessageAsJsonToStringInternal(message, &result); 131 if (!status.ok()) { 132 return status; 133 } 134 return result; 135 } 136 137 absl::Status ParseFromJsonStream( 138 google::protobuf::io::ZeroCopyInputStream* input, 139 const JsonParseOptions& options, google::protobuf::Message* message) { 140 auto resolver = 141 MakeTypeResolverForPool(message->GetDescriptor()->file()->pool()); 142 143 std::string binary; 144 google::protobuf::io::StringOutputStream output(&binary); 145 auto status = google::protobuf::util::JsonToBinaryStream( 146 resolver.get(), message->GetDescriptor()->full_name(), input, &output, 147 options); 148 149 if (!status.ok()) { 150 return absl::Status(static_cast<absl::StatusCode>(status.code()), 151 std::string(status.message())); 152 } 153 if (!message->ParseFromString(binary)) { 154 return absl::InvalidArgumentError( 155 "JSON transcoder produced invalid protobuf output."); 156 } 157 return absl::OkStatus(); 158 } 159 160 absl::Status ParseFromJsonStream( 161 google::protobuf::io::ZeroCopyInputStream* input, 162 google::protobuf::Message* message) { 163 return ParseFromJsonStream(input, DefaultParseOptions(), message); 164 } 165 166 absl::Status ParseFromJsonString(absl::string_view input, 167 const JsonParseOptions& options, 168 google::protobuf::Message* message) { 169 google::protobuf::io::ArrayInputStream stream(input.data(), input.size()); 170 return ParseFromJsonStream(&stream, options, message); 171 } 172 173 absl::Status ParseFromJsonString(absl::string_view input, 174 google::protobuf::Message* message) { 175 return ParseFromJsonString(input, DefaultParseOptions(), message); 176 } 177 178 void PackAny(const google::protobuf::Message& message, 179 absl::string_view type_uri, google::protobuf::Any* out) { 180 out->set_type_url(type_uri.data(), type_uri.size()); 181 message.SerializeToString(out->mutable_value()); 182 } 183 184 bool UnpackAny(const google::protobuf::Any& any, 185 google::protobuf::Message* result) { 186 google::protobuf::io::ArrayInputStream stream(any.value().data(), 187 any.value().size()); 188 google::protobuf::io::CodedInputStream coded_input_stream(&stream); 189 return result->ParseFromCodedStream(&coded_input_stream); 190 } 191 } // namespace kythe