【问题标题】:json: cannot unmarshal array into Go value of type struct [duplicate]json:无法将数组解组为结构类型的 Go 值 [重复]
【发布时间】:2020-04-29 02:56:06
【问题描述】:

我正在构建一个从 London Underground API 读取信息的应用程序。我正在努力将 GET 请求解析为可读的内容以及用户可以访问特定行信息的位置。

这是我当前的代码,我使用一个结构来存储解组后来自 GET 请求的响应。

// struct for decoding into a structure
    var tubeStatuses struct {
        object []struct {
            typeDef      []string `json:"$type"`
            idName       string   `json:"id"`
            name         string   `json:"name"`
            modeName     string   `json:"modeName"`
            disruptions  string   `json:"disruption"`
            created      string   `json:"created"`
            modified     string   `json:"modified"`
            statusObject []struct {
                zeroObject []struct {
                    typeDef        string `json:"$type"`
                    id             int    `json:"id"`
                    statusSeverity int    `json:"statusSeverity"`
                    statusDesc     string `json:"statusSeverityDescription"`
                    created        string `json:"created"`
                    validity       string `json:"validityPeriods"`
                }
            }
            route         string `json:"routeSections"`
            serviceObject []struct {
                zeroObject []struct {
                    typeDef string `json:"$type"`
                    name    string `json:"name"`
                    uri     string `json:"uri"`
                }
            }
            crowdingObject []struct {
                typeDef string `json:"$type"`
            }
        }
    }

    fmt.Println("Now retrieving Underground line status, please wait...")
    // two variables (response and error) which stores the response from e GET request
    getRequest, err := http.Get("https://api.tfl.gov.uk/line/mode/tube/status")
    fmt.Println("The status code is", getRequest.StatusCode, http.StatusText(getRequest.StatusCode))

    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }

    //close - this will be done at the end of the function
    // it's important to close the connection - we don't want the connection to leak
    defer getRequest.Body.Close()

    // read the body of the GET request
    rawData, err := ioutil.ReadAll(getRequest.Body)

    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }

    jsonErr := json.Unmarshal(rawData, &tubeStatuses)

    if jsonErr != nil {
        fmt.Println(jsonErr)
    }

    //test
    fmt.Println(tubeStatuses.object[0].name)

    fmt.Println("Welcome to the TfL Underground checker!\nPlease enter a number for the line you want to check!\n0 - Bakerloo\n1 - central\n2 - circle\n3 - district\n4 - hammersmith & City\n5 - jubilee\n6 - metropolitan\n7 - northern\n8 - piccadilly\n9 - victoria\n10 - waterloo & city")

我看到的错误如下:

json: cannot unmarshal array into Go value of type struct { object []struct { typeDef []string "json:\"$type\""; idName string "json:\"id\""; name string "json:\"name\""; modeName string "json:\"modeName\""; disruptions string "json:\"disruption\""; created string "json:\"created\""; modified string "json:\"modified\""; statusObject []struct { zeroObject []struct { typeDef string "json:\"$type\""; id int "json:\"id\""; statusSeverity int "json:\"statusSeverity\""; statusDesc string "json:\"statusSeverityDescription\""; created string "json:\"created\""; validity string "json:\"validityPeriods\"" } }; route string "json:\"routeSections\""; serviceObject []struct { zeroObject []struct { typeDef string "json:\"$type\""; name string "json:\"name\""; uri string "json:\"uri\"" } }; crowdingObject []struct { typeDef string "json:\"$type\"" } } }

如何将数组解组为可读的内容?

【问题讨论】:

  • 我在下面添加了一个答案,但也推荐查看 golang 格式和命名标准,可以在这里找到:golang.org/doc/effective_go.html Go 是一门很棒的语言。玩得开心!

标签: arrays json go struct


【解决方案1】:

以下是您的代码的工作版本。但是,您需要解决多个问题。

  1. 正如本线程其他地方所提到的,您应该将 TubeStatuses(注意大写)设置为一个类型和一个数组。字段名称也应大写,以便导出。

  2. 您没有考虑所有输出,并且在某些情况下类型错误。例如,您缺少一个名为“Disruption”的对象。我已经在我的示例中添加了它。 “拥挤”声明的类型错误。此外,“route”又名“routeSections”不是字符串。这是另一个数组,可能是对象,但 API 没有输出让我确定 routeSections 实际包含的内容。因此,作为一种解决方法,我将其声明为“json.RawMessage”的一种类型,以允许将其解组为字节片。详情请见:https://golang.org/pkg/encoding/json/#RawMessage

  3. 您不能使用json: "$type"`。具体来说,不允许使用美元符号。

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type TubeStatuses struct {
    TypeDef      []string `json:"type"`
    IDName       string   `json:"id"`
    Name         string   `json:"name"`
    ModeName     string   `json:"modeName"`
    Disruptions  string   `json:"disruption"`
    Created      string   `json:"created"`
    Modified     string   `json:"modified"`
    LineStatuses []struct {
        Status []struct {
            TypeDef                   string `json:"type"`
            ID                        int    `json:"id"`
            StatusSeverity            int    `json:"statusSeverity"`
            StatusSeverityDescription string `json:"statusSeverityDescription"`
            Created                   string `json:"created"`
            ValidityPeriods           []struct {
                Period struct {
                    TypeDef  string `json: "type"`
                    FromDate string `json: "fromDate"`
                    ToDate   string `json: "toDate"`
                    IsNow    bool   `json: "isNow"`
                }
            }
            Disruption struct {
                TypeDef             string   `json: "type"`
                Category            string   `json: "category"`
                CategoryDescription string   `json: "categoryDescription"`
                Description         string   `json: "description"`
                AdditionalInfo      string   `json: "additionalInfo"`
                Created             string   `json: "created"`
                AffectedRoutes      []string `json: "affectedRoutes"`
                AffectedStops       []string `json: "affectedStops"`
                ClosureText         string   `json: closureText"`
            }
        }
    }
    RouteSections json.RawMessage `json: "routeSections"`
    ServiceTypes  []struct {
        Service []struct {
            TypeDef string `json:"type"`
            Name    string `json:"name"`
            URI     string `json:"uri"`
        }
    }
    Crowding struct {
        TypeDef string `json:"type"`
    }
}

func main() {
    fmt.Println("Now retrieving Underground line status, please wait...")

    // two variables (response and error) which stores the response from e GET request
    getRequest, err := http.Get("https://api.tfl.gov.uk/line/mode/tube/status")
    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }

    fmt.Println("The status code is", getRequest.StatusCode, http.StatusText(getRequest.StatusCode))

    //close - this will be done at the end of the function
    // it's important to close the connection - we don't want the connection to leak
    defer getRequest.Body.Close()

    // read the body of the GET request
    rawData, err := ioutil.ReadAll(getRequest.Body)

    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }
    ts := []TubeStatuses{}

    jsonErr := json.Unmarshal(rawData, &ts)

    if jsonErr != nil {
        fmt.Println(jsonErr)
    }

    //test
    fmt.Println(ts[0].Name)

    fmt.Println("Welcome to the TfL Underground checker!\nPlease enter a number for the line you want to check!\n0 - Bakerloo\n1 - central\n2 - circle\n3 - district\n4 - hammersmith & City\n5 - jubilee\n6 - metropolitan\n7 - northern\n8 - piccadilly\n9 - victoria\n10 - waterloo & city")
}

输出...

【讨论】:

  • 非常感谢您的解释、链接和代码! ?你真的帮了我!
  • @JoshBl_ 不客气。 :)
【解决方案2】:

你需要一个该结构的数组:

var tubeStatuses []struct  {...}

此外,您的结构成员名称不会被导出,因此即使在您执行此操作后它们也不会被解组。名称大写。

您的结构很大,因此请考虑将其设为类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 2017-01-06
    • 2022-01-24
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多