如何在Go

时间:2019-07-09 21:21:16

标签: json go

我正在为API进行Go包装,我注意到其中两个JSON字段在没有任何数据时保持为空。 基本上,API会在给定的URL上返回一组信息,如果至少访问过一次,一切都会好起来的,我得到了一个完整的json,然后将其解编为一个结构:

{
   "stats":{
      "status":1,
      "date":"09.07.2019",
      "title":"Test",
      "devices":{
         "dev":[
            {
               "tag":"Desktop"
            }
         ],
         "sys":[
            {
               "tag":"GNU/Linux "
            },
            {
               "tag":"Windows 10"
            }
         ],
         "bro":[
            {
               "tag":"Firefox 67.0"
            },
            {
               "tag":"Chrome 62.0"
            }
         ]
      },
      "refs":[
         {
            "link":"www.google.com"
         }
      ]
   }
}

这是我正在使用的结构:

type Stats struct {
    Stats struct {
        Status  int    `json:"status"`
        Date    string `json:"date"`
        Title   string `json:"title"`
        Devices struct {
            Dev []struct {
                Tag string `json:"tag"`
            } `json:"dev"`
            Sys []struct {
                Tag string `json:"tag"`
            } `json:"sys"`
            Bro []struct {
                Tag string `json:"tag"`
            } `json:"bro"`
        } `json:"devices"`
        Refs []struct {
            Link string `json:"link"`
        } `json:"refs"`
    } `json:"stats"`
}

提供新的网址后,事情就会变得有些奇怪:

{
  "stats": {
    "status": 1,
    "date": "09.07.2019",
    "title": "Test2",
    "devices": [

    ],
    "refs": [

    ]
  }
}

如您所见,字段“ dev”,“ sys”和“ bro”只是消失了,因为它们没有被使用,当我尝试将JSON解组为同一结构时,我得到了json: cannot unmarshal array into Go struct field Stats.device of type [...] 我尝试使用两种不同的结构来处理两个响应,但是我敢肯定,有一种方法可以仅用一个结构就优雅地处理它们。 任何帮助将不胜感激,谢谢!

1 个答案:

答案 0 :(得分:0)

我终于设法通过一个丑陋的解决方法来使其工作。 我将结构更改为

type Stats struct {
    Status     int         `json:"status"`
    Date       string      `json:"date"`
    Title      string      `json:"title"`
    Devices    interface{} `json:"devices"`
    Refs       interface{} `json:"refs"`
}

然后,在两种情况下,我最终都可以解组JSON,但是在传递对象时得到map[string]interface{},在传递空数组时得到空interface{}。为了解决这种矛盾,我只是检查数据类型并强制使用JSON中间转换,以便将map[string]interface{}值解包到自定义Devices结构中:

// Devices contains devices information
type Devices struct {
    Dev []struct {
        Tag    string `json:"tag"`
        Clicks string `json:"clicks"`
    } `json:"dev"`
    Sys []struct {
        Tag    string `json:"tag"`
        Clicks string `json:"clicks"`
    } `json:"sys"`
    Bro []struct {
        Tag    string `json:"tag"`
        Clicks string `json:"clicks"`
    } `json:"bro"`
}

我使用的算法如下:

//ForceDevicesToRightType uses a json conversion as intermediary for filling the Stats.Devices
// struct with map[string]interface{} values
func ForceDevicesToRightType(dev interface{}) (Devices, error) {
    temp, err := json.Marshal(dev)
    if err != nil {
        return Devices{}, err
    }
    // Use a temporary variable of the right type
    var devices Devices
    err = json.Unmarshal(temp, &devices)
    if err != nil {
        return Devices{}, err
    }

    return devices, nil
}
// ForceRefsToRightType uses a json conversion as intermediary for filling the Stats.Refs
// struct with map[string]interface{} values
func ForceRefsToRightType(refs interface{}) (Refs, error) {
    temp, err := json.Marshal(refs)
    if err != nil {
        return Refs{}, err
    }
    // Use a temporary variable of the right type
    var references Refs
    err = json.Unmarshal(temp, &references)
    if err != nil {
        return Refs{}, err
    }

    return references, nil
}

由于编译器知道“设备”和“引用”字段均为interface{},所以在转换后我不能简单地访问任何方法,因此我只需对类型进行正确的转换就可以了。 例如,如果我想访问Dev子结构,这是正确的方法:

y, _ := GetStats()
fmt.Println(y.Devices.(Devices).Dev)

这很丑,但是行得通。

非常感谢您的帮助,希望这种方法可以使您免于头痛!