Parse JSON HTTP response using golang

huangapple go评论142阅读模式
英文:

Parse JSON HTTP response using golang

问题

我正在尝试从以下curl输出中获取"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, // 最长等待2秒
}

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"值的提示,我将不胜感激。

英文:

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

{  
  "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"

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

答案1

得分: 17

你可以创建与你的 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

英文:

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

答案2

得分: 7

一种方法是将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) {
    // 如果参数不是map,则忽略
    mobj, ok := obj.(map[string]interface{})
    if !ok {
        return nil, false
    }
    
    for k, v := range mobj {
        // 键匹配,返回值
        if k == key {
            return v, true
        }
        
        // 如果值是map,则递归搜索
        if m, ok := v.(map[string]interface{}); ok {
            if res, ok := find(m, key); ok {
                return res, true
            }
        }
        // 如果值是数组,则从每个元素中递归搜索
        if va, ok := v.([]interface{}); ok {
            for _, a := range va {
                if res, ok := find(a, key); ok {
                    return res, true
                }
            }
        }
    }
    
    // 未找到元素
    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找到一个可工作的示例。

英文:

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

答案3

得分: 3

通常在这种情况下,你会看到人们描述所有这些子struct类型。如果你实际上不需要重用任何子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")
}
英文:

Typically in these situations you will see people describe all of these sub struct types. If you don't actually need to reuse the definition of any sub structs (like as a type for a function argument), then you don't need to define them. You can just use one definition for the whole response. In addition, in some cases you don't 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")
}

答案4

得分: 2

你可以编写自己的解码器或使用现有的第三方解码器。例如,[github.com/buger/jsonparser][1] 可以通过迭代数组(两次)来解决你的问题。

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
[1]: http://github.com/buger/jsonparser

英文:

You can write your own decoder or use existing third-party decoders.
For instance, [github.com/buger/jsonparser][1] 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
[1]: http://github.com/buger/jsonparser

huangapple
  • 本文由 发表于 2017年8月18日 19:52:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/45756011.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定