golang自定義json encode

golang json自定義encode

簡介

golang原生提供了很方便的json處理,例如struct轉爲json時,直接在成員變量上使用 `json:"name"` 來設置json的字段。通常狀況下,對於基礎類型 json.Marshal(object) 均可以知足需求,但對於特殊狀況就須要經過自定義的json encode解決了。golang

特殊狀況

golang官方推薦提供的set方案是經過map來實現的json

custormSet := make(map [CustormStruct] bool)
custormObj := custorm.NextObj()
if _,ok := custormSet[custormObj];!ok{
    custormSet[custormObj] = true
}

經過這樣方法來實現集合的功能。app

問題是若是一個struct中須要一個set類型的成員變量,並須要轉爲json。若是直接使用 json.Marshal(object) ,對應類型就會變成一個map,而非咱們須要的元素不重複的list。測試

解決方法

案例簡介

統計全部訪問過本地某個端口的ip,具體數據採集的方法有好幾種,不是本文的重點就不介紹了。google

實現

type ListenPort struct{
	Port int
	ipSet map[string] bool
	ipList []*string
}

func NewPort(port int) ListenPort{
	ipSet := make(map[string] bool)
	ipList := []*string{}
	return ListenPort{
		port,
		ipSet,
		ipList,
	}
}

func (p *ListenPort)Add(ip string)  {
	if _,ok:= p.ipSet[ip];!ok{
		p.ipSet[ip] = true
		p.ipList = append(p.ipList,&ip)
	}
}

//敲黑板!!!這是重點
func (p ListenPort)MarshalJSON() ([]byte, error){
	return json.Marshal(&struct {
		Port int       `json:"port"`
		Ips  []*string `json:"ip"`
	}{
		Port: p.Port,
		Ips:  p.ipList,
	})
}

測試代碼

ipPort := NewPort(2333)
ipPort.Add("127.0.0.1")
ipPort.Add("116.211.174.177")
ipPort.Add("106.39.167.11")
ipPort.Add("220.181.57.2168")
ipPort.Add("127.0.0.1")
jsonbyte,err := json.Marshal(ipPort)
if err != nil {
	fmt.Println(err)
}
fmt.Println(string(jsonbyte))

說明

func (obj ObjectType)MarshalJSON() ([]byte, error) 方法應該是實現了json的某個接口。經過從新構造一個struct來解決這個問題。code

代碼是我原創的,方法是我google的orm

額外說明

  • 爲何不用反射?

經過實驗能夠發現對於reflect對於map,經過如下代碼獲得的就是一個key list。如下代碼打印的結果與打印 p.ipList 同樣接口

func (p *ListenPort)PrintlnIpList(){
    fmt.Println(reflect.ValueOf(p.ipSet).MapKeys())
}

通過嘗試發現,直接使用json轉換reflect.Value類型的變量獲得的是一個 {}ip

  • 爲何選擇增長一個slice?

實際上新增了slice儲存的是string的地址,並不會增長多少內存。對於reflect.Value類型的確有方法將其再次轉換成string類型。只不過每次獲取json轉換都須要遍歷一次slice,這須要本身權衡。內存

  • emmmm

ipSet 和ipList爲私有成員變量,純屬看業務場景須要

小結

~blablabla~

相關文章
相關標籤/搜索