1

I'm developing a gRPC User Service in Go and facing issues with unmarshalling JSON that includes a google.protobuf.BoolValue type field in the search criteria.

Here's my proto file:

syntax = "proto3";

package user;

import "google/protobuf/wrappers.proto";

option go_package = "/pb;user";

message User {
  int32 id = 1;
  string fname = 2;
  string city = 3;
  int64 phone = 4;
  float height = 5;
  bool married = 6;
}

message UserID {
  int32 id = 1;
}

message UserIDs {
  repeated int32 ids = 1;
}

message SearchCriteria {
  string city = 1;
  int64 phone = 2;
  google.protobuf.BoolValue married = 3;
}

message Users {
  repeated User users = 1;
}

service UserService {
  rpc GetUser(UserID) returns (User);
  rpc GetUsers(UserIDs) returns (Users);
  rpc SearchUsers(SearchCriteria) returns (Users);
}

Here’s the relevant part of my Go code for the server:

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "net"

    pb "golang-grcp-user-services/pb"

    _ "github.com/mattn/go-sqlite3"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "google.golang.org/protobuf/types/known/wrapperspb"
)

type server struct {
    pb.UnimplementedUserServiceServer
    db *sql.DB
}

func (s *server) SearchUsers(ctx context.Context, in *pb.SearchCriteria) (*pb.Users, error) {
    var users []*pb.User
    query := "SELECT id, fname, city, phone, height, married FROM users WHERE 1=1"
    var args []interface{}

    if in.City != "" {
        query += " AND city LIKE ?"
        args = append(args, "%"+in.City+"%")
    }
    if in.Phone != 0 {
        phoneStr := fmt.Sprintf("%d", in.Phone)
        query += " AND CAST(phone AS TEXT) LIKE ?"
        args = append(args, "%"+phoneStr+"%")
    }
    if in.Married != nil {
        query += " AND married = ?"
        args = append(args, in.Married.Value)
    }

    rows, err := s.db.Query(query, args...)
    if err != nil {
        return nil, fmt.Errorf("failed to execute query: %v", err)
    }
    defer rows.Close()

    for rows.Next() {
        var user pb.User
        err := rows.Scan(&user.Id, &user.Fname, &user.City, &user.Phone, &user.Height, &user.Married)
        if err != nil {
            return nil, fmt.Errorf("failed to scan row: %v", err)
        }
        users = append(users, &user)
    }
    if err := rows.Err(); err != nil {
        return nil, fmt.Errorf("error iterating rows: %v", err)
    }

    return &pb.Users{Users: users}, nil
}

When I test with grpcurl:

grpcurl -plaintext -d '{"phone": 9998887776, "married": {"value": false}}' localhost:50051 user.UserService/SearchUsers

I get the following error:

Error invoking method "user.UserService/SearchUsers": error getting request data: json: cannot unmarshal object into Go value of type bool

I have tried various approaches to resolve this issue, but none have worked so far. Any help or suggestions would be greatly appreciated!

1
  • 1
    I never worked with gprcurl, but did you try grpcurl -plaintext -d '{"phone": 9998887776, "married": false}' localhost:50051 user.UserService/SearchUsers instead? Have a look here: googleapis.github.io/gax-php/0.21.1/Google/Protobuf/…: "The JSON representation for BoolValue is JSON true and false"
    – NotX
    Commented Jul 7 at 13:24

1 Answer 1

1

@notx is correct.

The way to prove this to yourself is to perform the opposite action and generate JSON from SearchCriteria:

package main

import (
    "log/slog"
    "os"

    pb "golang-grcp-user-services/pb"
    "google.golang.org/protobuf/encoding/protojson"
    "google.golang.org/protobuf/types/known/wrapperspb"
)
func main() {
    sc := &pb.SearchCriteria{
        Phone: 9998887776,
        Married: &wrapperspb.BoolValue{
            Value: false,
        },
    }
    b, err := protojson.Marshal(sc)
    if err != nil {
        slog.Error("unable to marshal", "err", err)
        os.Exit(1)
    }

    slog.Info("JSON", "b", string(b))
}

Which yields:

{
  "phone": "9998887776",
  "married": false
}

NOTE Protobuf serializes phone (int64) as a string. Both numbers and strings are accepted by the marshaler. See JSON

Not the answer you're looking for? Browse other questions tagged or ask your own question.