问题
I've been trying to parse this JSON message from PubNub without any luck:
type PubNubMessage struct {
body []string
}
[[{"text":"hey"}],"1231212412423235","channelName"]
json: cannot unmarshal array into Go value of type main.PubNubMessage
Does anyone have an idea on how to decode such complex types in golang?
回答1:
The short answer is that you cannot directly unmarshal a JSON array of non-homogenous types (per your example) into a golang struct.
The long answer is that you should define an (m *PubNubMessage) UnmarshalJSON([]byte) error method for your PubNubMessage type which unmarshals the JSON string into an interface{}
and then uses type assertions to ensure the expected format and populates the structure.
For example:
type TextMessage struct {
Text string
}
type PubNubMessage struct {
Messages []TextMessage
Id string
Channel string
}
func (pnm *PubNubMessage) UnmarshalJSON(bs []byte) error {
var arr []interface{}
err := json.Unmarshal(bs, &arr)
if err != nil {
return err
}
messages := arr[0].([]interface{}) // TODO: proper type check.
pnm.Messages = make([]TextMessage, len(messages))
for i, m := range messages {
pnm.Messages[i].Text = m.(map[string]interface{})["text"].(string) // TODO: proper type check.
}
pnm.Id = arr[1].(string) // TODO: proper type check.
pnm.Channel = arr[2].(string) // TODO: proper type check.
return nil
}
// ...
jsonStr := `[[{"text":"hey"},{"text":"ok"}],"1231212412423235","channelName"]`
message := PubNubMessage{}
err := json.Unmarshal([]byte(jsonStr), &message)
回答2:
You can define UnmarshalJSON
on your PubNubMessage
to provide custom JSON deserialization. You probably should tweak this a little bit for your purposes, but the general idea is that you just unmarshal this json array into a slice, and then get all necessary parts from it.
Playground example here
回答3:
Your json is a heterogeneous array. You can at least define PubNubMessage
as either
type PubNubMessage []interface{}
and then access data with type assertions text:= (message[0].([]interface {})[0].(map[string]interface {}))["text"].(string)
working example https://play.golang.org/p/xhwbE2ora1
or type PubNubMessage []json.RawMessage
and after json.Unmarshal(jsonBlob, &message)
do 'json.Unmarshal(message[0], structured.text)' for each peace separately
https://play.golang.org/p/TJ0DfiweGo
回答4:
It is easier to use alternative to encoding/json
packages for parsing JSON arrays with values of distinct types. For instance, give a try to fastjson. It easily (and quickly) parses such arrays:
input := `[[{"text":"hey"}],"1231212412423235","channelName"]`
var p fastjson.Parser
v, err := p.Parse(input)
if err != nil {
log.Fatal(err)
}
a := v.GetArray()
for _, vv := range a {
switch vv.Type() {
case fastjson.TypeArray:
fmt.Printf("array %s\n", vv)
case fastjson.TypeString:
fmt.Printf("string %s\n", vv)
}
}
Additionally fastjson
provides handy functions for obtaining only the required fields from JSON:
// get v[0].text as Go byte slice
text := v.GetStringBytes("0", "text")
// get v[2]
channelName := v.Get("2") // this is the same as v.GetArray()[2]
来源:https://stackoverflow.com/questions/29348262/decoding-pubnub-messages-with-golang-json