开发十年,就只剩下这套Java开发体系了 >>>
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 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来解决这个问题。
代码是我原创的,方法是我google的
额外说明
- 为什么不用反射?
通过实验可以发现对于reflect对于map,通过以下代码得到的就是一个key list。以下代码打印的结果与打印 p.ipList 一样
func (p *ListenPort)PrintlnIpList(){
fmt.Println(reflect.ValueOf(p.ipSet).MapKeys())
}
经过尝试发现,直接使用json转换reflect.Value类型的变量得到的是一个 {}
- 为什么选择增加一个slice?
实际上新增了slice储存的是string的地址,并不会增加多少内存。对于reflect.Value类型的确有方法将其再次转换成string类型。只不过每次获取json转换都需要遍历一次slice,这需要自己权衡。
- emmmm
ipSet 和ipList为私有成员变量,纯属看业务场景需要
小结
~blablabla~