【问题标题】:Dealing with different types of parameters during Unmarshal在 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"`
    VPN              VPN       `json:"vpn"`
    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 上看到完整的源代码。

标签: json list go struct


【解决方案1】:

您可以实现json.Unmarshalerjson.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。有点丑,我知道...
    • 嗯,可以解决。我会尝试实现你的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多