简体   繁体   English

使用 golang 解析 JSON HTTP 响应

[英]Parse JSON HTTP response using golang

I am trying to get the value of say "ip" from my following curl output:我试图从我的以下 curl 输出中获取 say "ip" 的值:

{  
  "type":"example",
  "data":{  
    "name":"abc",
    "labels":{  
      "key":"value"
    }
  },
  "subsets":[  
    {  
      "addresses":[  
        {  
          "ip":"192.168.103.178"
        }
      ],
      "ports":[  
        {  
          "port":80
        }
      ]
    }
  ]
}

I have found many examples in the internet to parse json output of curl requests and I have written the following code, but that doesn't seem to return me the value of say "ip"我在互联网上找到了很多示例来解析 curl 请求的 json 输出,并且我编写了以下代码,但这似乎并没有返回给我说“ip”的值

package main

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

type svc struct {
    Ip string `json:"ip"`
}

func main() {

url := "http://myurl.com"

testClient := http.Client{
    Timeout: time.Second * 2, // Maximum of 2 secs
}

req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
    log.Fatal(err)
}


res, getErr := testClient.Do(req)
if getErr != nil {
    log.Fatal(getErr)
}

body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
    log.Fatal(readErr)
}

svc1 := svc{}
jsonErr := json.Unmarshal(body, &svc1)
if jsonErr != nil {
    log.Fatal(jsonErr)
}

fmt.Println(svc1.Ip)
}

I would appreciate if anyone could provide me hints on what I need to add to my code to get the value of say "ip".如果有人可以向我提供有关我需要添加到代码中以获得“ip”值的提示,我将不胜感激。

You can create structs which reflect your json structure and then decode your json.您可以创建反映您的 json 结构的结构,然后解码您的 json。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
)

type Example struct {
    Type    string   `json:"type,omitempty"`
    Subsets []Subset `json:"subsets,omitempty"`
}

type Subset struct {
    Addresses []Address `json:"addresses,omitempty"`
}

type Address struct {
    IP string `json:"IP,omitempty"`
}

    func main() {

    m := []byte(`{"type":"example","data": {"name": "abc","labels": {"key": "value"}},"subsets": [{"addresses": [{"ip": "192.168.103.178"}],"ports": [{"port": 80}]}]}`)

    r := bytes.NewReader(m)
    decoder := json.NewDecoder(r)

    val := &Example{}
    err := decoder.Decode(val)

    if err != nil {
        log.Fatal(err)
    }

    // If you want to read a response body
    // decoder := json.NewDecoder(res.Body)
    // err := decoder.Decode(val)

    // Subsets is a slice so you must loop over it
    for _, s := range val.Subsets {
        // within Subsets, address is also a slice
        // then you can access each IP from type Address
        for _, a := range s.Addresses {
            fmt.Println(a.IP)
        }
    }

}

The output would be: 192.168.103.178输出将是: 192.168.103.178

By decoding this to a struct, you can loop over any slice and not limit yourself to one IP通过将其解码为一个结构体,您可以遍历任何切片,而不会将自己限制在一个 IP 上

Example here:这里的例子:

https://play.golang.org/p/sWA9qBWljA https://play.golang.org/p/sWA9qBWljA

One approach is to unmarshal the JSON to a map , eg (assumes jsData contains JSON string)一种方法是将 JSON 解组为map ,例如(假设jsData包含 JSON 字符串)

obj := map[string]interface{}{}
if err := json.Unmarshal([]byte(jsData), &obj); err != nil {
    log.Fatal(err)
}

Next, implement a function for searching the value associated with a key from the map recursively, eg接下来,实现一个函数,用于从映射中递归搜索与键关联的值,例如

func find(obj interface{}, key string) (interface{}, bool) {
    //if the argument is not a map, ignore it
    mobj, ok := obj.(map[string]interface{})
    if !ok {
        return nil, false
    }

    for k, v := range mobj {
        //key match, return value
        if k == key {
            return v, true
        }

        //if the value is a map, search recursively
        if m, ok := v.(map[string]interface{}); ok {
            if res, ok := find(m, key); ok {
                return res, true
            }
        }
        //if the value is an array, search recursively 
        //from each element
        if va, ok := v.([]interface{}); ok {
            for _, a := range va {
                if res, ok := find(a, key); ok {
                    return res,true
                }
            }
        }
    }

    //element not found
    return nil,false
}

Note, that the above function return an interface{} .请注意,上述函数返回一个interface{} You need to convert it to appropriate type, eg using type switch:您需要将其转换为适当的类型,例如使用类型开关:

if ip, ok := find(obj, "ip"); ok {
    switch v := ip.(type) {
    case string:
        fmt.Printf("IP is a string -> %s\n", v)
    case fmt.Stringer:
        fmt.Printf("IP implements stringer interface -> %s\n", v.String())
    case int:

    default:
        fmt.Printf("IP = %v, ok = %v\n", ip, ok)
    }
}

A working example can be found at https://play.golang.org/p/O5NUi4J0iR可以在https://play.golang.org/p/O5NUi4J0iR找到一个工作示例

You can write your own decoder or use existing third-party decoders.您可以编写自己的解码器或使用现有的第三方解码器。 For instance, github.com/buger/jsonparser could solve your problem by iterating throw array (two times).例如, github.com/buger/jsonparser可以通过迭代 throw 数组(两次)来解决您的问题。

package main

import (
    "github.com/buger/jsonparser"
    "fmt"
)

var data =[]byte(`{
  "type":"example",
  "data":{
    "name":"abc",
    "labels":{
      "key":"value"
    }
  },
  "subsets":[
    {
      "addresses":[
        {
          "ip":"192.168.103.178"
        }
      ],
      "ports":[
        {
          "port":80
        }
      ]
    }
  ]
}`)

func main() {
    jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
        jsonparser.ArrayEach(value, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
            v, _, _, err := jsonparser.Get(value, "ip")
            if err != nil {
                return
            }
            fmt.Println("ip: ", string(v[:]))
        }, "addresses")
    }, "subsets")
}

Output: ip: 192.168.103.178输出: ip:192.168.103.178

Typically in these situations you will see people describe all of these sub struct types.通常在这些情况下,您会看到人们描述所有这些子struct类型。 If you dont actually need to reuse the definition of any sub struct s (like as a type for a function argument), then you dont need to define them.如果您实际上不需要重用任何 sub struct的定义(例如作为函数参数的类型),那么您不需要定义它们。 You can just use one definition for the whole response.您可以只对整个响应使用一个定义。 In addition, in some cases you dont need to define a type at all, you can just do it at the time of declaration:此外,在某些情况下,您根本不需要定义类型,您可以在声明时进行:

package main
import "encoding/json"

const s = `
{  
   "subsets": [
      {  
         "addresses": [ 
            {"ip": "192.168.103.178"}
         ]
      }
   ]
}
`

func main() {
   var svc struct {
      Subsets []struct {
         Addresses []struct { Ip string }
      }
   }
   json.Unmarshal([]byte(s), &svc)
   ip := svc.Subsets[0].Addresses[0].Ip
   println(ip == "192.168.103.178")
}

You can use the NewDecoder feature of the encoding/json package. 您可以使用encoding/json包的NewDecoder功能。

Like this: 像这样:

decoder := json.NewDecoder(req.Body)

err := decoder.Decode(&svc1)
if err != nil {
    log.Fatal(err)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM