這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
golang json自訂encode
簡介
golang原生提供了很方便的json處理,例如struct轉為json時,直接在成員變數上使用 `json:"name"` 來設定json的欄位。一般情況下,對於基礎類型 json.Marshal(object) 都可以滿足需求,但對於特殊情況就需要通過自訂的json encode解決了。
特殊情況
golang官方推薦提供的set方案是通過map來實現的
custormSet := make(map [CustormStruct] bool)custormObj := custorm.NextObj()if _,ok := custormSet[custormObj];!ok{ custormSet[custormObj] = true}
通過這樣方法來實現集合的功能。
問題是如果一個struct中需要一個set類型的成員變數,並需要轉為json。如果直接使用 json.Marshal(object) ,對應類型就會變成一個map,而非我們需要的元素不重複的list。
解決方案
案例簡介
統計所有訪問過本地某個連接埠的ip,具體資料擷取的方法有好幾種,不是本文的重點就不介紹了。
實現
type ListenPort struct{Port intipSet [string] boolipList []*string}type NewPort(port int) ListenPort{ipSet := make([string] bool)ipList := []*string{}return ListenPort{port,ipSet,ipList,}}func (p *ListenPort)Add(ip string) {if _,ok:= p.ipSet[ip];!ok{p.ipSet[ip] = truep.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來解決這個問題。
代碼是我原創的,方法是我google的
額外說明
通過實驗可以發現對於reflect對於map,通過以下代碼得到的就是一個key list。以下代碼列印的結果與列印 p.ipList 一樣
func (p *ListenPort)PrintlnIpList(){ fmt.Println(reflect.ValueOf(p.ipSet).MapKeys())}
經過嘗試發現,直接使用json轉換reflect.Value類型的變數得到的是一個 {}
實際上新增了slice儲存的是string的地址,並不會增加多少記憶體。對於reflect.Value類型的確有方法將其再次轉換成string類型。只不過每次擷取json轉換都需要遍曆一次slice,這需要自己權衡。
ipSet 和ipList為私人成員變數,純屬看業務情境需要
小結
~blablabla~