duanraa1984
2019-09-12 13:59
浏览 332
已采纳

如何在Proto3中为HTTP响应创建可空字段?

I want to return an object as an HTTP response where one of its fields is nullable. The problem is proto3 won't let me do it easily. This happens because I parsed a pointer of string to a string, so when the pointer points to null it produces this error runtime error: invalid memory address or nil pointer dereference

I have attempted to solve this by at least these two work-arounds I learned from the Internet.

1. Using oneof

exercise.proto (the message definition)

message ExercisesData {
    string Serial = 1 [json_name="serial"];
    string Title = 2 [json_name="title"];
    oneof OptionalSubmissionSerial {
       string SubmissionSerial = 3 [json_name="submission_serial"];
}

mapper.go (to parse a Go struct to fit the proto message)

exercise := &Exercise.ExercisesData {
           Serial:                   e.Serial,
           Title:                    e.Title,
           OptionalSubmissionSerial: &Exercise.ExercisesData_SubmissionSerial{
                SubmissionSerial: *e.SubmissionInfo.LatestSubmissionSerial,
           },
}

2. Using google/protobuf/wrappers.proto

exercise.proto (the message definition)

import "google/protobuf/wrappers.proto";

message ExercisesData {
    string Serial = 1 [json_name="serial"];
    string Title = 2 [json_name="title"];
    google.protobuf.StringValue SubmissionSerial = 3 [json_name="submission_serial"];
}

mapper.go (to parse a Go struct to fit the proto message)

exercise := &Exercise.ExercisesData {
           Serial:                   e.Serial,
           Title:                    e.Title,
           SubmissionSerial:         &wrappers.StringValue{
                Value: *e.SubmissionInfo.LatestSubmissionSerial,
            },
}

Expected Result

Both ways still produce the same error message, the only difference is the line of code it refers to. That's why I am so helpless. The expected HTTP response would look like this

{
    "status": "success",
    "data": [
        {
            "serial": "EXC-NT2OBHQT",
            "title": "Title of Topic Exercise",
            "submission_serial": null
        }
     ]
}

I really hope anyone can help me to find a way to define a nullable field in proto3 for a Http response and how to parse it from a struct. Thank you!

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • dsjpik057730 2019-09-13 11:25
    已采纳

    turns out I find another workaround that actually works! It's using google/protobuf/wrappers.proto but I gotta tweak it a lil' bit in the mapper. Here's how it goes:

    exercise.proto (the message definition)

    import "google/protobuf/wrappers.proto";
    
    message ExercisesData {
        string Serial = 1 [json_name="serial"];
        string Title = 2 [json_name="title"];
        google.protobuf.StringValue SubmissionSerial = 3 [json_name="submission_serial"];
    }
    

    mapper.go (to parse a Go struct to fit the proto message)

    import "github.com/golang/protobuf/ptypes/wrappers"
    
            exercise := &pbExercise.GetExercisesData{
                Serial:              e.Serial,
                Title:               e.Title,
            }
    
            if e.SubmissionInfo.LatestSubmissionSerial != nil {
                exercise.SubmissionSerial = &wrappers.StringValue{
                    Value: *e.LatestSubmissionSerial,
                }
            }
    
    已采纳该答案
    打赏 评论
  • duan7007 2019-09-12 17:33

    I assume you set syntax = proto3;. I don't think there is a way to achieve what you are after at this moment. Go check this issue.

    But if you are willing to live with

    {"serial":"serial","title":"title"}
    

    where submission_serial is altogether missing as opposed to

    {"serial":"serial","title":"title","submission_serial":null}
    

    than the following works just fine

    // ...
    data := &ExercisesData{Serial: "serial", Title: "title"}
    
    out, err := proto.Marshal(data)
    if err != nil {
      log.Fatalln("failed to encode: ", err)
    }
    
    buf := &bytes.Buffer{}
    marshaler := jsonpb.Marshaler{}
    
    err = marshaler.Marshal(buf, data)
    
    data2 := &ExercisesData{}
    if err := proto.Unmarshal(out, data2); err != nil {
      log.Fatalln("failed to parse: ", err)
    }
    // ...
    
    打赏 评论

相关推荐 更多相似问题