Jay D
Jay D

Reputation: 93

golang: "json: cannot unmarshal array into Go value of type string"

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)

Upvotes: 1

Views: 5122

Answers (1)

Suzu Hirose
Suzu Hirose

Reputation: 312

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

Upvotes: 1

Related Questions