node.jstypescriptprotocol-buffersgrpcgrpc-node

TypeScript / JavaScript gRPC google.protobuf.Struct cannot be read


I have a TypeScript server trying to read a JSON object using a Struct but it seems to be partially working only for objects containing a "fields" key which then expects an object as value. Nonetheless, a Struct should work with any JSON object.

Using BloomRPC I am trying the following message:

{
  "payload": {
    "fields": {
      "Hello": {
        "whatever": 0
      }
    }
  }
}

The server reads:

{ fields: { Hello: {} } }

If I send:

{
  "payload": {
    "anotherfield": {
      "HelloWorld": {
        "whatever": 0
      }
    }
  }
} 

I get an empty object on the server.

The simplified protobuf file looks like this:

syntax = "proto3";

import "google/protobuf/struct.proto";

// The service definition.
service TestTicketService {
  rpc UpdateTicket (UpdateTicketRequest) returns (UpdateTicketResponse);
}

// The request message containing the required ticket information.
message UpdateTicketRequest {
    string ticketId = 1;
    google.protobuf.Struct payload = 2;
}

// The response message containing any potential error message
message UpdateTicketResponse {
  string error = 1;
}

Any idea why google/protobuf/struct.proto doesn't work as expected?


Solution

  • The idea of a struct is that you can store arbitrary data - but only simple types: null, number, string, bool, array and object.

    This maps perfectly to JSON, and this is not by accident. The google.protobuf.Struct message has a special JSON representation:

    The JSON representation for Struct is JSON object.

    So you can parse any JSON string into a protobuf Struct, and when serializing to JSON again, you also get the same JSON string again.

    It is important to note that the in-memory representation of the parsed Struct is not equal to a JSON object. Protobuf does not have dynamic fields and has to represent JSON data in a more complicated manner. That is why struct.proto defines some other types.

    When you want to create a Struct in JavaScript, it is probably the easiest way to just create the JSON object you want:

    var jsonObject = {foo: "bar"};
    var jsonString = JSON.stringify(jsonObject);
    

    Now you can parse you Struct from this jsonObject or jsonString and put set resulting Struct as a field value in another protobuf message.

    Since you are already using TypeScript, it might be worth checking out one of the alternative TypeScript implementations for protobuf. I am the author of protobuf-ts. Creating a Struct is pretty straight-forward:

    let struct = Struct.fromJson({foo: "bar"});