Search code examples
jsongoprotocol-buffersgrpcgrpc-go

How to parse protocol buffer message and create json out of it?


Here is my minimal .proto file:

syntax = "proto3";

message getDhtParams {}

message DhtContents {
    string dht_contents=1;
}

service MyApp {
    rpc getDhtContent(getDhtParams) returns (DhtContents) {}
}

Two things to note related to the above proto file:

  1. It is a minimal file. There is a lot more to it.
  2. The server is already generated and running. The server is implemented in Python.

I am writing client in Go. And this is the fetching code I have come up with:

func fetchDht() (*pb.DhtContents, error) {
    // Set up a connection to the server.
    address := "localhost:9998"
    conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewMyAppClient(conn)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    r, err := client.GetDhtContent(ctx, &pb.GetDhtParams{})

    if err != nil {
        return nil, errors.New("could not get dht contents")
    }
    return r, nil
}

For sake of simplicity, I have tripped down the output, but the output looks something like this:

dht_contents:"{'node_ids': ['dgxydhlqoopevxv'], 'peer_addrs': [['192.168.1.154', '41457']], 'peer_meta': [{'peer_id': {'nodeID': 'dgxydhlqoopevxv', 'key': 'kdlvjdictuvgxspwkdizqryr', 'mid': 'isocvavbtzkxeigkkrubzkcx', 'public_key': 'uhapwxnfeqqmnojsaijghhic', '_address': 'xklqlebqngpkxb'}, 'ip_addrs': ['192.168.1.154', '41457'], 'services': [{'service_input': '', 'service_output': '', 'price': 0}], 'timestamp': 1661319968}]}"

A few things to note about this response:

  1. It starts with dht_contents: which I know is a field of DhtContents message.
  2. This could be an issue from the server side; in that case I will inform the service developer. But the json enclosed is not a valid JSON as it uses single quotes.

My questions:

  1. What is an elegant way to deal with that dht_contents? There must be the protobuf/grpc way. I aim to get the contents between double quotes.
  2. How do I convert the content to JSON? I have already created the struct to unmarshal.

It would be enough if I am also able to convert the response which is of type *pb.DhtContents to []byte, from there I can convert it to JSON.


Solution

    1. The generated code should have a method which will get rid of dht_contents:" from the start and " from the end.

    In your case, that method should be called GetDhtContents().

    You can modify your fetchDht function to something like this:

    func fetchDht() (string, error) {
        address := "localhost:9998"
        // ...
        if err != nil {
            return nil, errors.New("could not get dht contents")
        }
        return r.GetDhtContents(), nil
    }
    
    1. From there on, you can work on making it a valid JSON by replacing single quotes to double quotes. Or it may be handled on the service end.