英文:
Golang - Can't get array of objects after fetching JSON
问题
在向服务器发出请求后,我会得到以下类似的JSON数据:
{
"actions": [
{
"class": "...",
"parameters": [
{ ... },
{ ... }
]
}
]
...
}
我将这些数据存储在一个类型为map[string]interface{}
的变量中。
我想要访问actions
数组中第一个对象的parameters
数组。我可以成功地获取class
属性,即data.(map[string]interface{})["class"]
。
然而,如果我尝试获取parameters
属性,我得到的是nil
...
我尝试了data.(map[string][]interface{})["parameters"]
,但是我得到了错误panic: interface conversion: interface {} is map[string]interface {}, not map[string][]interface {}
。
你有什么想法,我在这里漏掉了什么?
编辑:
这是相应的Golang代码:
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := utils.GetJSON(theUrl, &jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// 获取顶层的"actions"属性..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
GetJSON
函数来自项目中的另一个文件:
func GetJSON(url string, result interface{}) error {
fmt.Printf("Getting JSON from %v\n", url)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("Cannot fetch URL %q: %v\n", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Bad resposne status code: %s\n", resp.Status)
}
// 尝试将JSON放入`result`中..
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return fmt.Errorf("Could not decode the JSON from response, err: %v\n", err)
}
return nil;
}
编辑2:
感谢评论中@larsks的建议-我创建了一个包含JSON字符串的最小代码示例,直接在main.go文件中,一切都正常工作。
然后,我再次在浏览器中尝试从URL直接获取数据,或者使用页面上的$.getJSON
方法获取数据,两者都返回相同的JSON数据。
然而,在我的Go代码中,当我转储JSON数据时,我看到actions
的第一个成员如下所示:
map[_class:hudson.model.ParametersDefinitionProperty parameterDefinitions:[map[...
所以当我尝试通过键parameterDefinitions
获取parameters
数组时,我得到的是对象数组... :O
所以...我不知道发生了什么...要么是Go本身在从后端获取数据时修改了JSON数据,要么是后端根据数据获取方式的不同返回了不同的内容。
(顺便说一下,后端是Jenkins API...所以我不知道为什么在Go中我得到的是parameterDefinitions
而不是parameters
... ...)
英文:
After I make a request to a server - I get JSON like this:
{
"actions": [
{
"class": "...",
"parameters": [
{ ... }
{ ... }
]
}
]
...
}
The type of the variable I've put this data in, is map[string]interface{}
.
I want to access the parameters
array in the first object of the actions
array. I can successfully get the class
property - data.(map[string]interface{})["class"]
.
However, if I try the same for the parameters
property - I get nil
...
I tried data.(map[string][]interface{})["parameters"]
- but I get error panic: interface conversion: interface {} is map[string]interface {}, not map[string][]interface {}
.
Any idea what I'm missing here?
EDIT:
The Golang code for this is this:
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := utils.GetJSON(theUrl, &jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// get the top level "actions" prop ..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
The GetJSON function is from another file in the project:
func GetJSON (url string, result interface{}) error {
fmt.Printf("Getting JSON from %v\n", url)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("Cannot fetch URL %q: %v\n", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Bad resposne status code: %s\n", resp.Status)
}
// attempt to put the JSON in the `result` ..
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return fmt.Errorf("Could not decode the JSON from response, err: %v\n", err)
}
return nil;
}
EDIT2:
Thanks to the ideas of @larsks in the comments - I created a minimal code sample with JSON in a string, directly in main.go file - and all worked fine.
Then I went ahead to the browser again and tried to fetch the data again - from directly hitting the URL or with $.getJSON
from a page - and both returned one and the same JSON data.
However, in my Go code, when I dump the JSON data - I see this for the first member of actions
:
map[_class:hudson.model.ParametersDefinitionProperty parameterDefinitions:[map[...
So when I try to get the parameters
array by the key parameterDefinitions
- then I get the array of object ... :O
Sooooo ... I don't know what's happening ... either Go itself modifies the JSON data when it gets it from the backend, or the backend itself returns different things, depending on how the data is being fetched.
(The backend is the Jenkins API by the way ... so I don't know why I get parameterDefinitions
instead of parameters
in Go ... ...)
答案1
得分: 1
感谢您更新问题;拥有正确的数据可以更容易地回答问题。
最好的方式是在您的问题中包含的代码是我们可以直接获取并运行的代码 - 这意味着它可以编译和运行,并且在给定您问题中的示例数据时,它会产生您所询问的行为。
我修改了您的代码,以便我可以在本地运行它,主要是通过将GetJSON
方法替换为从名为data.json
的文件中读取而不是访问API:
package main
import (
"encoding/json"
"fmt"
"os"
)
func GetJSON(result interface{}) error {
datafile, err := os.Open("data.json")
if err != nil {
panic(err)
}
return json.NewDecoder(datafile).Decode(&result)
}
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := GetJSON(&jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// get the top level "actions" prop ..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
如果我使用这个示例数据作为输入,我认为它与您在问题中展示的结构相匹配:
{
"actions": [
{
"class": "Name of class",
"parameters": [
{
"name": "alice",
"color": "blue"
},
{
"name": "bob",
"count": 10,
},
{
"name": "mallory",
"valid": false,
}
]
}
]
}
它会产生以下输出:
Class prop: /Name of class map[string]interface {}
Parameters prop: /[map[color:blue name:alice] map[count:10 name:bob] map[name:mallory valid:false]] map[string]interface {}
这不会为parameters
键产生nil
值。为了更全面地回答您的问题,您能否更新它,以便它具有能够重现您所询问的行为的示例数据和代码?
英文:
Thanks for updating your question; having the correct data makes it easier to answer.
It's best if the code you include in your question is something we can just grab and run -- that means it compiles and runs, and when given the sample data in your question, it produces the behavior you're asking about.
I've modified your code so that I can run it locally, primarily by replacing the GetJSON
method with something that reads from a file named data.json
instead of hitting an API:
package main
import (
"encoding/json"
"fmt"
"os"
)
func GetJSON(result interface{}) error {
datafile, err := os.Open("data.json")
if err != nil {
panic(err)
}
return json.NewDecoder(datafile).Decode(&result)
}
func main() {
var jsonResult map[string]interface{}
errorFromJsonFetching := GetJSON(&jsonResult)
if errorFromJsonFetching != nil {
fmt.Printf("Error from checking deploy build: %#v\n", errorFromJsonFetching)
}
// get the top level "actions" prop ..
actionZero := jsonResult["actions"].([]interface{})[0]
fmt.Printf("Class prop: /%v %T\n", actionZero.(map[string]interface{})["class"], actionZero)
fmt.Printf("Parameters prop: /%v %T\n", actionZero.(map[string]interface{})["parameters"], actionZero)
}
If I feed it this sample data, which I believe matches the structure of what you show in your question:
{
"actions": [
{
"class": "Name of class",
"parameters": [
{
"name": "alice",
"color": "blue"
},
{
"name": "bob",
"count": 10,
},
{
"name": "mallory",
"valid": false,
}
]
}
]
}
It produces the following output:
Class prop: /Name of class map[string]interface {}
Parameters prop: /[map[color:blue name:alice] map[count:10 name:bob] map[name:mallory valid:false]] map[string]interface {}
This doesn't produce a nil
value for the parameters
key. In order to more fully answer your question, can you update it so that it has sample data and code that reproduces the behavior you're asking baout?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论