簡體   English   中英

使用 golang 解析 JSON HTTP 響應

[英]Parse JSON HTTP response using golang

我試圖從我的以下 curl 輸出中獲取 say "ip" 的值:

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

我在互聯網上找到了很多示例來解析 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)
}

如果有人可以向我提供有關我需要添加到代碼中以獲得“ip”值的提示,我將不勝感激。

您可以創建反映您的 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)
        }
    }

}

輸出將是: 192.168.103.178

通過將其解碼為一個結構體,您可以遍歷任何切片,而不會將自己限制在一個 IP 上

這里的例子:

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

一種方法是將 JSON 解組為map ,例如(假設jsData包含 JSON 字符串)

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

接下來,實現一個函數,用於從映射中遞歸搜索與鍵關聯的值,例如

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
}

請注意,上述函數返回一個interface{} 您需要將其轉換為適當的類型,例如使用類型開關:

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)
    }
}

可以在https://play.golang.org/p/O5NUi4J0iR找到一個工作示例

您可以編寫自己的解碼器或使用現有的第三方解碼器。 例如, 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")
}

輸出: ip:192.168.103.178

通常在這些情況下,您會看到人們描述所有這些子struct類型。 如果您實際上不需要重用任何 sub struct的定義(例如作為函數參數的類型),那么您不需要定義它們。 您可以只對整個響應使用一個定義。 此外,在某些情況下,您根本不需要定義類型,您可以在聲明時進行:

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")
}

您可以使用encoding/json包的NewDecoder功能。

像這樣:

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