在Golang中解码任意的JSON。

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

Decode arbitrary JSON in Golang

问题

我对在Go中解码任意JSON对象/消息有一个问题。假设你有三个完全不同的JSON对象(也称为消息),你可以在HTTP连接上接收到它们,我们称它们为:

{ home : { 一些唯一的数组、对象、字段和数组对象 } }

{ bike : { 一些唯一的数组、对象、字段和数组对象 } }

{ soda : { 一些唯一的数组、对象、字段和数组对象 } }

我的想法是你可以将它们解码为一个接口类型的映射,例如:

func httpServerHandler(w http.ResponseWriter, r *http.Request) {
    message := make(map[string]interface{})
    decoder := json.NewDecoder(r.Body)
    _ = decoder.Decode(&message)

然后使用if、else if块来查找有效的JSON消息:

if _, ok := message["home"]; ok {
    // 将interface{}解码为适当的结构体
} else if _, ok := message["bike"]; ok {
    // 将interface{}解码为适当的结构体
} else {
    // 将interface{}解码为适当的结构体
}

现在,在if块中,如果我重新解码整个包,它可以正常工作,但我认为这有点浪费,因为我已经部分解码了它,只需要解码映射的值,即一个interface{},但我似乎无法正确地做到这一点。

重新解码整个内容确实可以工作,例如,如果我定义了一个名为homeType的结构体:

var homeObject homeType
var bikeObject bikeType
var sodaObject sodaType

然后在if块中执行以下操作:

if _, ok := message["home"]; ok {
    err = json.Unmarshal(r.Body, &homeObject)
    if err != nil {
        fmt.Println("Bad Response, unable to decode JSON message contents")
        os.Exit(1)
    }

因此,在不重新解码/解组整个内容的情况下,你如何处理映射中的interface{}呢?

英文:

I have a question about decoding arbitrary JSON objects/messages in Go.. Lets say for example you have three wildly different JSON objects (aka messages) that you can receive on an http connection, lets call them for sake of illustration:

{ home : { some unique set of arrays, objects, fields, and arrays objects } }

and

{ bike : { some unique set of arrays, objects, fields, and arrays objects } }

and

{ soda : { some unique set of arrays, objects, fields, and arrays objects } }

What I am thinking is you could decode these, from an http connection in to a map of interfaces such as:

func httpServerHandler(w http.ResponseWriter, r *http.Request) {
    message := make(map[string]interface{})
    decoder := json.NewDecoder(r.Body)
    _ = decoder.Decode(&message)

Then do an if, else if block to look for valid JSON messages

if _, ok := message["home"]; ok {
    // Decode interface{} to appropriate struct
} else if _, ok := message["bike"]; ok {
    // Decode interface{} to appropriate struct
} else {
    // Decode interface{} to appropriate struct
}

Now in the if block I can make it work if I re-decode the entire package, but I was thinking that is kind of a waste since I have already partially decoded it and would only need to decode the value of the map which is an interface{}, but I can not seem to get that to work right.

Redecoding the entire thing works though, if I do something like the following where the homeType for example is a struct:

var homeObject homeType
var bikeObject bikeType
var sodaObject sodaType

Then in the if block do:

if _, ok := message["home"]; ok {
	err = json.Unmarshal(r.Body, &homeObject)
	if err != nil {
		fmt.Println("Bad Response, unable to decode JSON message contents")
		os.Exit(1)
	}

So without re-decoding / unmarshal-ing the entire thing again, how do you work with the interface{} in a map?

答案1

得分: 2

如果你有类似于map[string]interface{}的结构,你可以使用类型断言来访问值,例如:

home, valid := msg["home"].(string)
if !valid {
    return
}

这对于简单的值非常有效。对于更复杂的嵌套结构,你可能会发现使用json.RawMessage进行延迟解码或实现自定义的json.Unmarshaler更容易。可以参考这篇文章进行详细讨论。

另一个想法是定义一个自定义的Message类型,其中包含指向HomeBikeSoda结构体的指针,例如:

type Home struct {
    HomeStuff     int
    MoreHomeStuff string
} 

type Bike struct {
    BikeStuff int
}

type Message struct {
    Bike *Bike `json:"Bike,omitempty"`
    Home *Home `json:"Home,omitempty"`
}

如果将它们设置为nil时省略,则解组只会填充相关的结构体。你可以在这里进行测试。

英文:

If you have something like a map[string]interface{} then you can access the values using type assertions, e.g.

home, valid := msg["home"].(string)
if !valid {
	return
}

This works nicely for simple values. For more complicated nested structures you might find it easier to do deferred decoding with json.RawMessage or implement a custom json.Unmarshaler. See this for a very detailed discussion.

Another idea might be to define a custom Message type consisting of pointers to Home, Bike, and Soda structs. Such as

type Home struct {
    HomeStuff     int
    MoreHomeStuff string
} 

type Bike struct {
    BikeStuff int
}

type Message struct {
    Bike *Bike `json:"Bike,omitempty"`
	Home *Home `json:"Home,omitempty"`
}

If you set these to omit if nil then unmarshalling should only populate the relevant one. You can play with it here.

huangapple
  • 本文由 发表于 2015年5月12日 01:43:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/30174220.html
匿名

发表评论

匿名网友

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

确定