1

I have a protobuf message

message Event {
   string type = 1;
   string names = 2;
}

The type for ‘names’ has recently changed from string to []string in the underlying data source where older data is still a string but new data is going to be []string. So, when the data is returned, some records can have string and the rest can have []string. I need to customize the unmarshaling (jsonpb.Unmarshaler is being used to unmarshal json.Rawmessage to Event type) and convert the ‘names’ to a string if it’s an []string (by concatenating the values from array/slice) in order to keep the response format consistent so client doesn't break.

I get this error while unmarshaling:

json: cannot unmarshal array into Go value of type string

Can anyone please suggest on how this can be done?

I tried google.protobuf.Any like below but couldn’t completely figure the unmarshaling part.

message Event {
   string type = 1;
   google.protobuf.Any names = 2;
}

Unmarshall snippet:

evnt := new(pb.Event)
unmarshaler    = jsonpb.Unmarshaler{AllowUnknownFields: true}
unmarshaler.Unmarshal(bytes.NewReader(*<json.RawMessage>*), evnt)
2
  • 1
    You will probably need a completely custom unmarshaller for this type. Do you have such a func? Commented Jun 29, 2022 at 19:51
  • Not yet. I'm trying to figure out a customer unmarshaller but don't have much experience working with proto types yet. Will update if I get it to work. Commented Jun 29, 2022 at 20:31

1 Answer 1

1

Make the name into a type then give it a custom UnmarshalJSON method. Example:

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strings"
)

type name struct {
    New []string
    Old string
}

type event struct {
    Type string `json:"type"`
    Name name   `json:"name"`
}

func do(j string) {
    var e event
    err := json.Unmarshal([]byte(j), &e)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Failed to unmarshall %s: %s\n", j, err)
        return
    }
    fmt.Printf("%v\n", e)
}

func (n *name) UnmarshalJSON(text []byte) error {
    t := strings.TrimSpace(string(text))
    if strings.HasPrefix(t, "[") {
        return json.Unmarshal(text, &n.New)
    }
    return json.Unmarshal(text, &n.Old)
}

func main() {
    jsonstring := `{"type":"a","name":"rodney"}`
    jsonarray := `{"type":"a","name":["rodney","dangerfield"]}`
    do(jsonstring)
    do(jsonarray)
}

Playground: https://go.dev/play/p/sNtUZKHC_Rt

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.