golang自定义json encode

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 [string] bool
	ipList []*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] = 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~

猜你喜欢

转载自my.oschina.net/u/3703365/blog/1813135