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~

相关文章
相关标签/搜索