使用golang JSON解码PubNub消息

huangapple go评论81阅读模式
英文:

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&#39;ve been trying to parse this JSON message from PubNub without any luck:

    type PubNubMessage struct {
        body []string
    }

    [[{&quot;text&quot;:&quot;hey&quot;}],&quot;1231212412423235&quot;,&quot;channelName&quot;]
    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, &amp;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{})[&quot;text&quot;].(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 := `[[{&quot;text&quot;:&quot;hey&quot;},{&quot;text&quot;:&quot;ok&quot;}],&quot;1231212412423235&quot;,&quot;channelName&quot;]`
message := PubNubMessage{}
err := json.Unmarshal([]byte(jsonStr), &amp;message)

答案2

得分: 1

你的JSON是一个异构数组。你可以将PubNubMessage定义为以下两种方式之一:

  1. type PubNubMessage []interface{},然后使用类型断言来访问数据,例如:text := (message[0].([]interface{})[0].(map[string]interface{}))["text"].(string)。你可以在这个示例中看到工作示例。

  2. 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 {}))[&quot;text&quot;].(string)
working example https://play.golang.org/p/xhwbE2ora1

or type PubNubMessage []json.RawMessage and after json.Unmarshal(jsonBlob, &amp;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 := `[[{&quot;text&quot;:&quot;hey&quot;}],&quot;1231212412423235&quot;,&quot;channelName&quot;]`
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(&quot;array %s\n&quot;, vv)
case fastjson.TypeString:
fmt.Printf(&quot;string %s\n&quot;, 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(&quot;0&quot;, &quot;text&quot;)
// get v[2]
channelName := v.Get(&quot;2&quot;)  // this is the same as v.GetArray()[2]

huangapple
  • 本文由 发表于 2015年3月30日 22:03:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/29348262.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定