英文:
Reading and Unmarshalling API results in Golang
问题
在下面的程序中,我从一个API中提取了一些数据。
它输出的是一个相当复杂的数据。
当我使用ioutil.ReadAll(resp.Body)
时,结果的类型是[]uint8
。
如果我尝试读取结果,它只是一个随机的整数数组。
然而,如果我使用string(diskinfo)
将其转换为字符串,我就能读取它。
但是我想在一个结构体中使用它,并且在解组时遇到了困难。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
)
type ApiResults struct {
Results []struct {
StatementID int `json:"statement_id"`
Series []struct {
Name string `json:"name"`
Tags struct {
Host string `json:"host"`
} `json:"tags"`
Columns []string `json:"columns"`
Values []interface{} `json:"values"`
} `json:"series"`
} `json:"results"`
}
func main() {
myURL := "my_url"
myQuery := fmt.Sprintf("my_query")
resp, err := http.Get(myURL + url.QueryEscape(myQuery))
if err != nil {
fmt.Printf("ERROR: %v\n", err)
} else {
fmt.Println(reflect.TypeOf(resp))
diskInfo, _ := ioutil.ReadAll(resp.Body)
fmt.Println(reflect.TypeOf(diskInfo))
fmt.Println(diskInfo)
fmt.Println(string(diskInfo))
diskInfoString := string(diskInfo)
data := ApiResults{}
json.Unmarshal([]byte(diskInfoString), &data)
//fmt.Printf("Values = %v\n", data.Results[0].Series[0].Values)
//fmt.Printf("Server = %v\n", data.Results[0].Series[0].Tags.Host)
}
}
如果我将数据视为字符串,我会得到以下结果(格式化后):
{
"results": [
{
"statement_id": 0,
"series": [
{
"name": "disk",
"tags": {
"host": "myServer1"
},
"columns": [
"time",
"disk_size"
],
"values": [
[
"2021-07-07T07:53:32.291490387Z",
1044
]
]
},
{
"name": "disk",
"tags": {
"host": "myServer2"
},
"columns": [
"time",
"disk_size"
],
"values": [
[
"2021-07-07T07:53:32.291490387Z",
1046
]
]
}
]
}
]
}
我认为我的ApiResults
结构体也结构不正确,因为API的结果包含了多个主机的信息。
但首先,我怀疑数据是否必须以不同的格式发送到结构体中。一旦我完成这一步,我可能可以尝试弄清楚如何从结构体中读取数据。
英文:
In the below program I'm extracting some data from an API.
It outputs a rather complex data.
When I ioutil.ReadAll(resp.Body)
, the result is of type []uint8
.
If I try to read the results, its just a random array of integers.
However, I'm able to read it if I convert it to string using string(diskinfo)
But I want to use this in a Struct and having trouble unmarshalling.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
)
type ApiResults struct {
results []struct {
statement_id int `json.statement_id`
series []struct {
name string `json.name`
tags struct {
host string `json.host`
}
columns []string `json.columns`
values []interface{} `json.values`
}
}
}
func main() {
my_url := "my_url"
my_qry := fmt.Sprintf("my_query")
resp, err := http.Get(my_url + url.QueryEscape(my_qry))
if err != nil {
fmt.Printf("ERROR: %v\n", err)
} else {
fmt.Println(reflect.TypeOf(resp))
diskinfo, _ := ioutil.ReadAll(resp.Body)
fmt.Println(reflect.TypeOf((diskinfo)))
fmt.Println(diskinfo)
fmt.Println(string(diskinfo))
diskinfo_string := string(diskinfo)
data := ApiResults{}
json.Unmarshal([]byte(diskinfo_string), &data)
//fmt.Printf("Values = %v\n", data.results.series.values)
//fmt.Printf("Server = %v\n", data.results.series.tags.host)
}
}
If I view the data as a string, I get this (formatted):
{"results":[
{"statement_id":0,
"series":[
{"name":"disk",
"tags":{"host":"myServer1"},
"columns":["time","disk_size"],
"values":[["2021-07-07T07:53:32.291490387Z",1044]]},
{"name":"disk",
"tags":{"host":"myServer2"},
"columns":["time","disk_size"],
"values":[["2021-07-07T07:53:32.291490387Z",1046]]}
]}
]}
I think my Apireturn
struct is also structured incorrectly because the API results have info for multiple hosts.
But first, I doubt if the data has to be sent in a different format to the struct. Once I do this, I can probably try to figure out how to read from the Struct next.
答案1
得分: 2
ioutil.ReadAll
已经以byte[]
类型的形式提供了数据。因此,你可以直接调用json.Unmarshal
并将其作为参数传递。
import (
"encoding/json"
"io/ioutil"
"net/http"
)
func toStruct(res *http.Response) (*ApiResults, error) {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
defer res.Body.Close()
data := ApiResults{}
if err := json.Unmarshal(body, &data); err != nil {
return nil, err
}
return data, nil
}
你的结构体似乎也存在问题。使用结构体标签的正确方式如下所示。此外,字段需要被导出以供json.Unmarshal
使用的json标签正常工作——以大写字母开头即可。
type ApiResults struct {
Results []struct {
StatementId int `json:"statement_id"`
Series []struct {
Name string `json:"name"`
Tags struct {
Host string `json:"host"`
} `json:"tags"`
Columns []string `json:"columns"`
Values []interface{} `json:"values"`
} `json:"series"`
} `json:"results"`
}
英文:
The ioutil.ReadAll
already provides you the data in the type byte[]
. Therefore you can just call json.Unmarshal
passing it as a parameter.
import (
"encoding/json"
"io/ioutil"
"net/http"
)
func toStruct(res *http.Response) (*ApiResults, error) {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
defer res.Body.Close()
data := ApiResults{}
if err := json.Unmarshal(body, &data); err != nil {
return nil, err
}
return data, nil
}
There also seems to be an issue with your struct. The correct way to use struct tags is as follows. Plus, fields need to be exported for the json tag (used by json.Umarshal
) to work – starting with uppercase will do it.
type ApiResults struct {
Results []struct {
StatementId int `json:"statement_id"`
Series []struct {
Name string `json:"name"`
Tags struct {
Host string `json:"host"`
} `json:"tags"`
Columns []string `json:"columns"`
Values []interface{} `json:"values"`
} `json:"series"`
} `json:"results"`
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论