英文:
Decoding PubNub messages with golang JSON
问题
我一直在尝试解析来自PubNub的JSON消息,但没有成功:
type PubNubMessage struct {
body []string
}
[[{"text":"hey"}],"1231212412423235","channelName"]
json: 无法将数组解组为类型为main.PubNubMessage的Go值
有人知道如何在golang中解码这种复杂类型吗?
<details>
<summary>英文:</summary>
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?
</details>
# 答案1
**得分**: 1
简短的回答是,你不能直接将一个非同类型的 JSON 数组(根据你的示例)反序列化为一个 Golang 结构体。
长的回答是,你应该为你的 PubNubMessage 类型定义一个 [`(m *PubNubMessage) UnmarshalJSON([]byte) error` 方法](https://golang.org/src/encoding/json/decode.go#L81),该方法将 JSON 字符串反序列化为一个 `interface{}`,然后使用类型断言来确保期望的格式并填充结构体。
例如:
```go
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)
以上是一个示例,你可以根据你的实际需求进行修改。
英文:
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
得分: 1
你的JSON是一个异构数组。你可以将PubNubMessage
定义为以下两种方式之一:
-
type PubNubMessage []interface{}
,然后使用类型断言来访问数据,例如:text := (message[0].([]interface{})[0].(map[string]interface{}))["text"].(string)
。你可以在这个示例中看到工作示例。 -
type PubNubMessage []json.RawMessage
,在执行json.Unmarshal(jsonBlob, &message)
之后,对每个部分分别使用json.Unmarshal(message[0], &structured.text)
。你可以在这个示例中看到具体的实现。
英文:
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
答案3
得分: 0
你可以在PubNubMessage
上定义UnmarshalJSON
方法来提供自定义的JSON反序列化。你可能需要根据你的需求稍微调整一下,但是基本思路是将这个JSON数组解析成一个切片,然后从中获取所有必要的部分。
这里是一个Playground示例。
英文:
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
答案4
得分: 0
使用encoding/json
包解析具有不同类型值的JSON数组可能会比较麻烦。你可以尝试使用fastjson作为替代方案。它可以轻松(且快速)地解析这种数组:
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)
}
}
此外,fastjson
还提供了方便的函数,用于从JSON中获取所需的字段:
// 获取 v[0].text 作为 Go 字节切片
text := v.GetStringBytes("0", "text")
// 获取 v[2]
channelName := v.Get("2") // 这与 v.GetArray()[2] 是相同的
以上是翻译好的内容,请确认是否满意。
英文:
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]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论