Parse JSON HTTP response using golang

后端 未结 4 1564
有刺的猬
有刺的猬 2020-12-19 09:25

I am trying to get the value of say \"ip\" from my following curl output:

{  
  \"type\":\"example\",
  \"data\":{  
    \"name\":\"abc\",
    \"labels\":{           


        
相关标签:
4条回答
  • 2020-12-19 09:30

    You can create structs which reflect your json structure and then decode your 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

    By decoding this to a struct, you can loop over any slice and not limit yourself to one IP

    Example here:

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

    0 讨论(0)
  • 2020-12-19 09:33

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

    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

    0 讨论(0)
  • 2020-12-19 09:41

    You can use the NewDecoder feature of the encoding/json package.

    Like this:

    decoder := json.NewDecoder(req.Body)
    
    err := decoder.Decode(&svc1)
    if err != nil {
        log.Fatal(err)
    }
    
    0 讨论(0)
  • 2020-12-19 09:56

    One approach is to unmarshal the JSON to a map, e.g. (assumes jsData contains JSON string)

    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, e.g.

    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{}. You need to convert it to appropriate type, e.g. 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

    0 讨论(0)
提交回复
热议问题