在 Unmarshal 期间处理不同类型的参数
Posted
技术标签:
【中文标题】在 Unmarshal 期间处理不同类型的参数【英文标题】:Dealing with different types of parameters during Unmarshal 【发布时间】:2021-04-04 15:36:00 【问题描述】:我正在使用我的 Fritz!Box 路由器中的一些 API,我想在一个体面的结构中解组 json 响应,只需要找到一个好的方法来做到这一点。
有时在 API 响应中 WLan 参数是布尔值,有时是这种类型的对象
// WLan contains info about the Wireless Lan
type WLan struct
Txt string `json:"txt"`
Led string `json:"led"`
Title string `json:"title"`
Link string `json:"link"`
Tooltip string `json:"tooltip"`
如果您需要有关代码的更多信息,可以使用github repo。
我需要添加布尔 wlan 参数,我尝试复制“数据”结构并更改名称,但该解决方案对我来说听起来很糟糕。
Wlan 包含在这个结构中:
// Data contains data about the Fritz!Box
type Data struct
NasLink string `json:"naslink"`
FritzOS FritzOS `json:"fritzos"`
Webdav int `json:"webdav,string"`
Manual string `json:"MANUAL_URL"`
Language string `json:"language"`
AVM string `json:"AVM_URL"`
USBConnect string `json:"usbconnect"`
Foncalls Foncalls `json:"foncalls"`
*** *** `json:"***"`
Internet Internet `json:"internet"`
DSL DSL `json:"dsl"`
ServicePortalURL string `json:"SERVICEPORTAL_URL"`
Comfort Comfort `json:"comfort"`
Changelog Changelog `json:"changelog"`
TamCalls TamCalls `json:"tamcalls"`
Lan External `json:"lan"`
USB External `json:"usb"`
FonNum External `json:"fonnum"`
NewsURL string `json:"NEWSLETTER_URL"`
Net Net `json:"net"`
Dect External `json:"dect"`
WLan WLan `json:"wlan"`
//Wlan bool `json:"wlan"` # This is the other "case"
【问题讨论】:
这是在另一个结构中使用吗?如果是这样,也包括封闭结构。有多种方法可以解决此问题,但您需要在解组完成后阐明您想要什么。 是的,WLAN 包含在数据中。数据将“wlan”名称解组为 WLan,但有时不是 json 对象,而是一个 bool 值。无论如何你可以在 github 上看到完整的源代码。 【参考方案1】:您可以实现json.Unmarshaler
和json.Marshaler
接口。
type WLan struct
Bool *bool `json:"-"`
Txt string `json:"txt"`
Led string `json:"led"`
Title string `json:"title"`
Link string `json:"link"`
Tooltip string `json:"tooltip"`
// implements json.Unmarshaler
func (w *WLan) UnmarshalJSON(data []byte) error
if len(data) > 0 && (data[0] == 't' || data[0] == 'f') // seems to be a bool
w.Bool = new(bool)
return json.Unmarshal(data, w.Bool)
if len(data) > 1 && data[0] == '' && data[len(data)-1] == '' // it's an object
// type W and the conversion (*W)(w) are required to
// prevent encoding/json from invoking the UnmarshalJSON
// method recursively causing a stack overflow
type W WLan
return json.Unmarshal(data, (*W)(w))
return nil // or error, up to you
// implements json.Marshaler
func (w WLan) MarshalJSON() ([]byte, error)
if w.Bool != nil
return json.Marshal(*w.Bool)
// Same as with UnmarshalJSON, type W and the conversion W(w) are
// required to prevent encoding/json from invoking the MarshalJSON
// method recursively causing a stack overflow
type W WLan
return json.Marshal(W(w))
https://play.golang.org/p/s72zt4ny7Pv
【讨论】:
【参考方案2】:我不知道这是否是一个好的解决方案,我还是新手,但无论如何,您可以使用 json.RawMessage
并将 wlan
属性的解组“延迟”到两个单独的结构字段之一中.例如:
package main
import (
"encoding/json"
"fmt"
)
// Data contains data about the Fritz!Box. (other fields omitted for brevity)
type Data struct
Language string `json:"language"`
NewsURL string `json:"NEWSLETTER_URL"`
WLanRaw *json.RawMessage `json:"wlan"`
WLanBool bool `json:"-"`
WLanInfo *WLanInfo `json:"-"`
// WLanInfo contains infos about the Wireless Lan
type WLanInfo struct
Txt string `json:"txt"`
Led string `json:"led"`
Title string `json:"title"`
Link string `json:"link"`
Tooltip string `json:"tooltip"`
func UnmarshalData(raw []byte, data *Data) error
if err := json.Unmarshal(raw, data); err != nil
return err
switch string(*data.WLanRaw)
case "true", "false":
json.Unmarshal(*data.WLanRaw, &data.WLanBool)
default:
if err := json.Unmarshal(*data.WLanRaw, &data.WLanInfo); err != nil
return err
return nil
func main()
jsonBool := []byte(`
"language": "it",
"NEWSLETTER_URL": "https://example.com/news",
"wlan": true
`)
jsonInfo := []byte(`
"language": "it",
"NEWSLETTER_URL": "https://example.com/news",
"wlan":
"txt": "footxt",
"led": "fooled",
"title": "hello",
"link": "bar",
"tooltip": "baz"
`)
// error handling omitted
var dataBool Data
UnmarshalData(jsonBool, &dataBool)
fmt.Printf("%+v\n\n", dataBool)
var dataInfo Data
UnmarshalData(jsonInfo, &dataInfo)
fmt.Printf("%+v %+v\n", dataInfo, dataInfo.WLanInfo)
$ go build fritz.go
$ ./fritz
Language:it NewsURL:https://example.com/news WLanRaw:0xc0000a4060 WLanBool:true WLanInfo:<nil>
Language:it NewsURL:https://example.com/news WLanRaw:0xc0000a4080 WLanBool:false WLanInfo:0xc0000b0000 &Txt:footxt Led:fooled Title:hello Link:bar Tooltip:baz
$
【讨论】:
可能是一个解决方案,但是当有人检查“bool”属性时会导致问题,因为它总是错误的 @LucaDametto 当wlan
属性是 json 中的对象时,它是“总是false
”。好吧,只有当WLanInfo
== nil
时才应该检查WLanBool
。有点丑,我知道...
嗯,可以解决。我会尝试实现你的例子。以上是关于在 Unmarshal 期间处理不同类型的参数的主要内容,如果未能解决你的问题,请参考以下文章
golang Marshal / Unmarshal将不同的JSON对象转换为Go结构