json - 在 GO 中反序列化一个非标准的 json
问题描述
我正在编写一个简单的应用程序,它向返回类型的异构 JSON 的 API 发出请求
{
"results": [
[123, "Zho's Mask", 10586],
[345, "Ravaging Superior Studded Coat", 58]
]
}
最终,我希望能够使用结果响应中的特定索引。例如,我希望能够获得“Zho's Mask”,或者价格:10586。这是我正在使用的 API GW2TP。
GO JSON 博客中的大多数示例都引用了不包含嵌套数组的更简单或直接的 JSON。
根据我的阅读,由于我知道 JSON 响应的一般外观,我可以制作一个 GO 结构并将其解组为该结构的一个实例。但是,我无法为这项工作创建正确的结构。
这是我目前创建适当结构的尝试
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
// {"results":[[123,"Zho's Mask",3532]]}
type Equipment struct {
Results []ResArray
}
type ResArray struct {
Medium []LastArray
}
type LastArray struct {
Info string
}
func main() {
res, err := http.Get("http://api.gw2tp.com/1/items?ids=123&fields=name,sell")
if err != nil {
log.Fatal(err)
}
var equipment Equipment
data, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Print("ReadAll Error: ", err, "\n")
}
err = json.Unmarshal(data, &equipment)
if err != nil {
fmt.Print("Unmarshal error: ", err, "\n")
}
}
这是解组错误:
Unmarshal error: json: cannot unmarshal array into Go struct field Equipment.Results of type main.ResArray
最后,这是我目前 在 Golang an Introduction 中实现所需的 GO 结构 JSON方法的灵感。
解决方案
类型 ResArray 结构 {
但它不是结构,而是切片!
type LastArray struct { Info string }
但它不是字符串,有时是字符串,有时是数字。
执行此操作的简单方法是将您的类型定义为
type Equipment struct {
Results [][]interface{}
}
它说结果包含一片……某物。您可以命名中间类型,但这不是必需的。那么例如e.Results[0][1].(string)
将是"Zho's Mask"
。
更好的方法是通过提供一个 custom来实现UnmarshalerUnmarshalJSON
接口,如下所示:
type Equipment struct {
Results []Item
}
type Item struct {
ID int
Name string
Sell int
}
func (i *Item) UnmarshalJSON(b []byte) error {
// We're deserializing into a struct, but in JSON it's a mixed-type array.
var arr []interface{}
err := json.Unmarshal(b, &arr)
if err != nil {
return fmt.Errorf("unmarshal Item underlying array: %w", err)
}
if len(arr) != 3 {
return fmt.Errorf("Item underlying array should be 3 elements, got %d", len(arr))
}
// JSON numbers will become float64 when loaded into interface{} but we want int
id, ok := arr[0].(float64)
if !ok {
return fmt.Errorf("expected float64 for Item.ID, got %T", arr[0])
}
i.ID = int(id)
i.Name, ok = arr[1].(string)
if !ok {
return fmt.Errorf("expected string for Item.Name, got %T", arr[1])
}
sell, ok := arr[2].(float64)
if !ok {
return fmt.Errorf("expected float64 for Item.Sell, got %T", arr[2])
}
i.Sell = int(sell)
return nil
}
请记住,这些类型与您向 API 请求的确切字段列表结合在一起——如果您更改它,您将不得不更改类型和从数组加载它的解组函数。
推荐阅读
- python - 数据框在一个“单元格”中获取所有数据
- testing - 使用自动构建构建工作流
- javascript - 使用 AJAX 在 iframe 中放置 videoID
- javascript - 在本机反应中隐藏动态生成的按钮
- c# - 有没有办法在 .NET 4 中使用 C# 6 或更高版本?
- json - AdaptiveCardsJson - 点击通话选项而不打开新 URL
- php - 使用存储库模式编写 REST API 的函数 update() 出现问题
- python - 使用类自变量作为默认类方法参数
- cmd - 根据文件类型扩展名更改文件名
- microsoft-cognitive - HoloLens 上的 onnx 1.2 模型(自定义视觉)