Работа с различными типами параметров во время разборки

#json #list #go #struct

# #json #Список #Вперед #структура

Вопрос:

Я работаю с некоторыми API-интерфейсами от моего Fritz!Box router, я бы хотел разобрать ответ json в приличной структуре, просто нужно найти хороший способ сделать это.

Иногда в ответе API параметром WLan является bool, в других случаях это объект этого типа

 // 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.

Мне нужно добавить логический параметр wlan, я попытался дублировать структуру «Data» и изменить имя, но это решение звучит для меня очень плохо.

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"
}
 

Комментарии:

1. Используется ли это в другой структуре? Если это так, включите также включающую структуру. Это можно решить несколькими способами, но вам нужно уточнить, что вы хотите, после завершения демаршализации.

2. Да, WLan содержится в данных. Данные разархивируют имя «wlan» на WLan, но иногда вместо объекта json используется значение bool. В любом случае вы можете увидеть полный исходный код на github.

Ответ №1:

Я не знаю, хорошее ли это решение, я все еще новичок, но в любом случае, вы могли бы использовать 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, amp;data.WLanBool)
    default:
        if err := json.Unmarshal(*data.WLanRaw, amp;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, amp;dataBool)
    fmt.Printf("% vnn", dataBool)

    var dataInfo Data
    UnmarshalData(jsonInfo, amp;dataInfo)
    fmt.Printf("% v % vn", 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} amp;{Txt:footxt Led:fooled Title:hello Link:bar Tooltip:baz}
$
 

Комментарии:

1. Может быть решением, но вызовет проблемы, когда кто-то проверяет свойство «bool», поскольку оно всегда будет false

2. @LucaDametto это «всегда false «, когда wlan свойство является объектом в json. ну, WLanBool должно проверяться только в том случае, если WLanInfo == nil . довольно некрасиво, я знаю…

3. Ммм, может быть решением. Я постараюсь реализовать ваш пример.

Ответ №2:

Вы могли бы реализовать интерфейсы json.Unmarshaler and 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 amp;amp; (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 amp;amp; data[0] == '{' amp;amp; 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