Protocol Buufersのproto3ではJsonフォーマットとの交換が簡単にできます。
プロトコル定義ファイルに記述した型とJsonの型対応に従ってProtocol Buffersにエンコードされ、またその逆のデコードもできます。
(下の例ではgRPCのHelloWorldサンプルに少し手を加えています。)
リソース
Protocol BuffersとJsonの型対応表
https://developers.google.com/protocol-buffers/docs/proto3#json
Protocol Buffersのjson_util関数一覧
https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.util.json_util
環境
$ protoc --version
libprotoc 3.6.1
$ clang++ --version
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
protoファイル
HelloWorld.proto
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
サーバ側コード
server.cxx
#include <iostream>
#include <memory>
#include <string>
#include <grpc++/grpc++.h>
#include "helloworld.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("localhost:50051");
GreeterServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
クライアント側コード
#include <iostream>
#include <memory>
#include <grpc++/grpc++.h>
#include <google/protobuf/util/json_util.h>
#include "helloworld.grpc.pb.h"
using namespace std;
using namespace grpc;
int main(int argc, char ** argv) {
std::shared_ptr<grpc::Channel> channel = CreateChannel("localhost:50051", InsecureChannelCredentials());
std::unique_ptr<helloworld::Greeter::Stub> stub = helloworld::Greeter::NewStub(channel);
ClientContext context;
helloworld::HelloRequest request;
helloworld::HelloReply reply;
google::protobuf::util::JsonStringToMessage("{\"name\":\"Hello, Hello, Hello, gRPC\"}", &request);
request.set_name(request.name() + " adding string");
std::cout << "grpc calling.." << std::endl;
stub->SayHello(&context, request, &reply);
std::cout << "grpc called.." << std::endl;
std::string outstr;
google::protobuf::util::MessageToJsonString(reply, &outstr);
std::cout << "decode to json format.:" << outstr.c_str() << ":" << std::endl;
return 0;
}
Makefile
Makefile
TARG1 = server
SRCS1 = server.cxx helloworld.grpc.pb.cc helloworld.pb.cc
TARG2 = client
SRCS2 = client.cxx helloworld.grpc.pb.cc helloworld.pb.cc
TARG = $(TARG1) $(TARG2)
LIBS += -pthread -lprotobuf -lgrpc++
FLAGS += -std=c++11 -Wno-unused-parameter -fno-strict-aliasing -fsanitize=address -fno-omit-frame-pointer
LIBDS += -L/usr/lib/llvm-10/lib -L/usr/include/lib
all: $(TARG)
helloworld.pb.cc: helloworld.proto
protoc -I=. --cpp_out=. ./helloworld.proto
helloworld.grpc.pb.cc: helloworld.proto
protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc=/usr/bin/grpc_cpp_plugin ./helloworld.proto
$(TARG1): $(SRCS1)
clang++ $(SRCS1) $(OPTS) $(FLAGS) $(INCS) $(LIBDS) $(LIBS) -o $(TARG1)
$(TARG2): $(SRCS2)
clang++ $(SRCS2) $(OPTS) $(FLAGS) $(INCS) $(LIBDS) $(LIBS) -o $(TARG2)
実行結果
サーバ側
$ ./server
Server listening on localhost:50051
クライアント側
$ ./client
grpc calling..
grpc called..
decode to json format.:{"message":"Hello Hello, Hello, Hello, gRPC adding string"}: