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