Va5ili5
Va5ili5

Reputation: 798

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?

Upvotes: 3

Views: 9163

Answers (3)

Mamrezo
Mamrezo

Reputation: 1509

First, install @types/google-protobuf and:

  let rqm = new UpdateTicketRequest();
  rqm.setTicketId("1");
  rqm.setPayload(Struct.fromJavaScript({
    Hello:{
      whatever: 0,
    }
  });
  //and call the api....
  UpdateTicket(rqm);

Upvotes: 2

Timo Stamm
Timo Stamm

Reputation: 750

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"});

Upvotes: 3

Va5ili5
Va5ili5

Reputation: 798

What really confused me is that I was trying to pass normal JSON objects and expecting to read them. The whole point is that from the client side, the JSON object needs to be encoded in a very specific way.

For example:

"payload": {
    "fields": {
      "name": {
        "stringValue": "joe"
      },
      "age": {
        "numberValue": 28
      }
    }
  }

You can figure out the format of the message by looking at the Struct proto file here: https://googleapis.dev/nodejs/asset/latest/v1_doc_google_protobuf_doc_struct.js.html

Upvotes: 5

Related Questions