【问题标题】:Is there a way to extract JSON from an http response without having to build structs?有没有一种方法可以从 http 响应中提取 JSON 而无需构建结构?
【发布时间】:2017-01-23 21:24:06
【问题描述】:

我看到的所有方式都涉及构建结构并将数据解组到结构中。但是,如果我收到包含数百个字段的 JSON 响应怎么办?我不想为了获得我想要的数据而创建 100 个字段结构。来自 Java 背景,有一些简单的方法可以简单地将 http 响应作为字符串获取,然后将 JSON 字符串传递给允许轻松遍历的 JSON 对象。这是非常无痛的。 Go中有这样的东西吗?

伪代码中的Java示例:

String json = httpResponse.getBody();
JsonObject object = new JsonObject(json); 
object.get("desiredKey");

【问题讨论】:

  • 大部分答案都涉及不支持索引的接口,因此访问 JSON 键的数组值中包含的元素不会像编组到结构那样简单。
  • @hermancain 我们不能索引接口本身,但是我们可以从接口中获取具体的类型(例如切片),然后我们可以使用索引。

标签: json go


【解决方案1】:

Golang:从 HTTP 响应中获取 JSON,而不使用结构作为帮助器

这是我们遇到的典型场景。这是通过json.Unmarshal 实现的。

这是一个简单的json

{"textfield":"I'm a text.","num":1234,"list":[1,2,3]}

被序列化以通过网络发送并在 Golang 端解组。

package main

import (
    "fmt"
    "encoding/json"
)

func main() {
    // replace this by fetching actual response body
    responseBody := `{"textfield":"I'm a text.","num":1234,"list":[1,2,3]}`
    var data map[string]interface{}
    err := json.Unmarshal([]byte(responseBody), &data)
    if err != nil {
        panic(err)
    }
    fmt.Println(data["list"])
    fmt.Println(data["textfield"])
}

希望这对您有所帮助。

【讨论】:

  • 链接获取实际响应对象stackoverflow.com/questions/38807903/…
  • 对于像我这样的新手:data["list"], data["textfield"], ... 是接口,所以你可以使用类型断言来获取具体类型。即,data["list"].([]interface{})[0] 将为 1(这也是一个接口,其具体类型为int)。为什么[]interface{}?因为json.Unmarshal 将 JSON 数组存储到 []interface{} 中。请参阅 Unmarshal 的文档。
【解决方案2】:

json.Unmarshal 方法将解组到一个不包含原始 JSON 对象中所有字段的结构。换句话说,你可以挑选你的领域。下面是一个示例,其中 FirstName 和 LastName 是精心挑选的,并且 MiddleName 从 json 字符串中被忽略:

package main

import (
  "encoding/json"
  "fmt"
)

type Person struct {
  FirstName string `json:"first_name"`
  LastName  string `json:"last_name"`
}

func main() {
  jsonString := []byte("{\"first_name\": \"John\", \"last_name\": \"Doe\", \"middle_name\": \"Anderson\"}")

  var person Person
  if err := json.Unmarshal(jsonString, &person); err != nil {
    panic(err)
  }

  fmt.Println(person)
}

【讨论】:

    【解决方案3】:

    您也可以将其解组为 map[string]interface{}

    body, err := ioutil.ReadAll(resp.Body)
    map := &map[string]interface{}{}
    json.Unmarshal(body, map)
    desiredValue := map["desiredKey"]
    

    接收到的json必须有一个对象作为最外层元素。该地图还可以包含列表或嵌套地图,具体取决于 json。

    【讨论】:

    • 不起作用:一开始map不接受作为变量,所以我把它改成了map_。但是,在最后一行出现了这个错误:invalid operation: map_["data"] (type *map[string]interface {} does not support indexing)
    • @BenyaminJafari 您需要取消引用,即(*map_)["your_key"]
    【解决方案4】:

    这里的其他答案具有误导性,因为它们没有向您展示如果您尝试更深入地了解地图会发生什么。这个例子工作得很好:

    package main
    
    import (
       "encoding/json"
       "fmt"
       "net/http"
    )
    
    func main() {
       r, e := http.Get("https://github.com/manifest.json")
       if e != nil {
          panic(e)
       }
       body := map[string]interface{}{}
       json.NewDecoder(r.Body).Decode(&body)
       /*
       [map[
          id:com.github.android
          platform:play
          url:https://play.google.com/store/apps/details?id=com.github.android
       ]]
       */
       fmt.Println(body["related_applications"])
    }
    

    但如果你尝试更深一层,它会失败:

    /*
    invalid operation: body["related_applications"][0] (type interface {} does not
    support indexing)
    */
    fmt.Println(body["related_applications"][0])
    

    相反,您需要在每个深度级别断言类型:

    /*
    map[
       id:com.github.android
       platform:play
       url:https://play.google.com/store/apps/details?id=com.github.android
    ]
    */
    fmt.Println(body["related_applications"].([]interface{})[0])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-30
      • 1970-01-01
      • 2022-08-08
      • 1970-01-01
      • 2022-11-18
      • 1970-01-01
      • 2014-01-02
      • 1970-01-01
      相关资源
      最近更新 更多