当前位置: 首页 > 知识库问答 >
问题:

如何在golang中解析JSON而无需两次解组

松新
2023-03-14

我有一个web套接字连接,它在JSON对象中发送不同类型的消息,我想将内容解组到一些已知的结构中。为此,我想我应该做以下几点:

步骤1)将JSON解组到通用映射[字符串]接口{}

第2步)找到我要找的钥匙

步骤3)尝试将值强制转换为我的类型之一(此操作失败)

步骤3(可选)json封送此值并将其解组到我的已知结构

如果我尝试使用myStruct,ok:=value。(myType)它将失败,但如果我使用json。封送(值),然后是json。对我的结构来说,它工作得很好。我应该这样做吗?去json-

示例代码

https://play.golang.org/p/to_0Id_ja9

package main

import (
    "encoding/json"
    "fmt"
)

type Ping struct {
    Ping string `json:"ping"`
}

type Ack struct {
    Messages []Message `json:"messages"`
}

type Message string

func main() {
    testJSON := []byte(`{"ack":{"messages":["Hi there","Hi again"]}}`)
    var myAck = Ack{}
    var myMap map[string]interface{}
    err := json.Unmarshal(testJSON, &myMap)
    if err != nil {
        fmt.Println("error unmarshalling: ", err)
    }
    for k, v := range myMap {
        fmt.Printf("key: %s, value: %s \n", k, v)

        switch k {
        case "ping":
            fmt.Println(k, " is a ping", v)
        case "ack":
            fmt.Println(k, " is an ack containing a message list")
            ackjson, err := json.Marshal(v)
            if err != nil {
                fmt.Println("marshal error: ", err)
            }
            err = json.Unmarshal(ackjson, &myAck)
            if err != nil {
                fmt.Println("unmarshal error", err)
            } else {
                fmt.Println("New ack object: ", myAck)
            }
        default:
            fmt.Printf("%s is of a type (%T) I don't know how to handle", k, v)
        }
    }
}

共有3个答案

赫连越
2023-03-14

另一种解决方案是在所有消息(例如:type)之间使用一个公共键,该键指示消息的类型。在此之后,如果消息不够复杂,则可以从其他映射键/类型断言映射值构建原始结构:

从我编写的一些相关websocket代码中可以看出:https://github.com/blaskovicz/cut-me-some-slack/blob/master/chat/message.go#L66

func DecodeClientMessage(c *ClientMessage) (typedMessage interface{}, err error) {
    buff := map[string]string{}
    err = json.NewDecoder(bytes.NewReader(c.Raw)).Decode(&buff)
    if err != nil {
        return
    }

    switch t := buff["type"]; t {
    case "message":
        channelID := buff["channel_id"]
        if channelID == "" {
            err = fmt.Errorf("invalid client message received: missing channel_id")
            return
        }
        cms := &ClientMessageSend{ChannelID: channelID, Text: buff["text"]}
        if cms.Text == "" {
            err = fmt.Errorf("invalid client message received: missing text")
        } else {
            typedMessage = cms
        }

... 然后调用方代码执行结果。(类型)断言和切换:https://github.com/blaskovicz/cut-me-some-slack/blob/master/chat/hub.go#L150

switch m := raw.(type) {
case *ClientMessageHistory:
    channelID := h.resolveSlackChannel(m.ChannelID)
    if channelID == "" {
        log.Printf("error: no channel found matching %s\n", m.ChannelID)
        return
    }
    var username string
    if c.Client.User != nil {
        username = c.Client.User.Username
    } else {
        username = "<anonymous>"
    }
    log.Printf("sending previous messages for channel %s to client %s\n", channelID, username)
    for _, prevMessage := range h.previousMessages(channelID, m.Limit) {
        c.Client.send <- prevMessage
    }
相旭
2023-03-14

您需要创建一个包含所有可能接收到的密钥的结构,然后解组一次,最后根据哪些密钥是nil您可以决定得到的是什么类型的数据包。

下面的示例显示此行为:https://play.golang.org/p/aFG6M0SPJs

package main

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


type Ack struct {
  Messages []string `json:"messages"`
}

type Packet struct {
  Ack  * Ack    `json:"ack"`
  Ping * string `json:"ping"`
}

func runTest(testJSON []byte) {
  var p = Packet{}
  err := json.Unmarshal(testJSON, &p)
  if err != nil {
    fmt.Println("error unmarshalling: ", err)
  }
  if (p.Ack != nil) {
    fmt.Println("Got ACK: ", strings.Join(p.Ack.Messages, ", "));
  } else if (p.Ping != nil){
          fmt.Println("Got PING");
  }
}

func main() {
  tests := [][]byte{
    []byte(`{"ack":{"messages":["Hi there","Hi again"]}}`),
    []byte(`{"ping": "Are you there?"}`),
  }
  for _, test := range tests {
    runTest(test)
  }

}
王兴腾
2023-03-14

一种解决方案是通过将值解组为json来部分解组数据。RawMessage而不是接口{}

var myMap map[string]json.RawMessage

在稍后的开关中,这仍然是必需的,不需要列表。只要做:

err = json.Unmarshal(v, &myAck)

游乐场:https://play.golang.org/p/NHd3uH5e7z

 类似资料:
  • 问题内容: 输出: 我有api调用返回的结果,它就像对象数组(json数组)的复杂格式。我需要通过golang解析该结果,我该如何实现?以下数据是我的结果,它是从api调用获取的。 问题答案: 您仅错过了一点:您需要导出结构的字段: 它将起作用(在Go Playground上尝试): 请注意,JSON文本包含带有小写字母文本的字段名称,但是包足够“聪明”以匹配它们。如果它们完全不同,则可以使用st

  • 问题内容: 如何在Android中解析如下数组? 如您所见,没有像其他示例数组那样定义数组的键,例如: 对于第二个数组,我可以轻松地使用并使用: 但是,很明显,第一个数组没有键。那么,如何处理呢?标准的Android库甚至可能吗? 问题答案: 您是否尝试过这样做?

  • 问题内容: 我似乎找不到使用Dart语言在没有密钥的情况下解析json数组的解决方案。我所能找到的就是使用Java。我需要解析这样的内容。 Java解决方案来自这里 如果我有重复的问题,请告知我。谢谢! 问题答案: 只需json.decode正常使用,例如: 碰巧的成员l都将int小号

  • 问题内容: 我正在使用一个API,该API返回如下所示的JSON 在Swift中,我使用两个函数来获取并解析JSON 然后我用 可以很好地解析JSON。当我打印出 我得到了数组的所有内容。但是,我无法访问每个单独的索引。我很肯定这是一个数组,因为我之间 返回正确的长度。但是,如果我尝试通过使用访问单个索引 XCode关闭语法高亮显示并给我以下信息: 并且代码将无法编译。 这是XCode 6的错误,

  • 问题内容: 在python中,您可以获取json对象并从中获取特定项目,而无需声明结构,将其保存到结构中,然后像Go中那样获取值。有没有一种包装或更简单的方法来存储Go中来自json的特定值? 蟒蛇 走 问题答案: 您可以解码为,然后按键获取元素。 通常首选结构,因为它们对类型更明确。您只需要在所需的JSON中声明字段,而无需像使用映射(隐式编码/ json处理)那样键入assert值。

  • 问题内容: 我在s3中有很多gzip日志文件,其中有3种类型的日志行:b,c,i。我和c都是单层json: 类型b是深度嵌套的json。我碰到这个要点,谈论要编译一个罐子来完成这项工作。由于我的Java技能还不算出色,因此我真的不知道该怎么做。 由于类型i和c并不总是相同的顺序,因此很难在生成正则表达式中指定所有内容。Pig是否可以处理JSON(在gzip文件中)?我正在使用在Amazon Ela