ProtocolBuffersとJsonのエンコード/デコード

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
## 環境
``` shell-session
$ 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
``` shell-session 
syntax = "proto3";

package helloworld;

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}
```
## サーバ側コード
server.cxx
``` c++
#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;
}
```
## クライアント側コード
``` c++
#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
``` 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)
```
## 実行結果
### サーバ側
``` shell-session
$ ./server 
Server listening on localhost:50051
```
### クライアント側
``` shell-session
$ ./client 
grpc calling..
grpc called..
decode to json format.:{"message":"Hello Hello, Hello, Hello, gRPC adding string"}:
```