MarshalJSON没有调用、无效?
问题
今天我在golang自定义json的序列化时,发现我的struct实现MarshalJSON()竟然没有效果,还是按照默认的方式来序列化。这让我非常郁闷☹,后来我查找了一些资料发现这个问题不简单。。
下面是我有问题的代码
|
|
pointer receiver 和value receiver
|
|
上面两个方法分别就是pointer receiver和value receiver
由于golang没有面向对象那一套东西,上面的两行代码可以视为下面的形式
|
|
所以在golang中value变量只能调用value receiver的方法,pointer只能调用pointer receiver的方法。比如:
|
|
其实我们实际可以相互调用的
|
|
因为golang的底层帮我们做了一层隐式转换
t1.pointerMethod() == (&t1).pointerMethod()
t2.valueMethod()==(*t2).valueMethod()
总的来说
T可以调用value receiver和pointer receiver方法*T如果是可以取地址(addressable)的变量,也是可以调用value receiver和pointer receiver方法- 如果
*T是不可去地址的变量,那只能调用pointer receiver方法
关于什么是可以去地址的变量,什么是不可以取地址。可以参考这篇文章链接
Interface的value receiver 和pointer receiver
在讨论interface的receiver之前,先看下interface在使用中的一些问题。
|
|
上面的代码为什么pointer可以赋值给i,而value却不行?
因为pointer变量可以通过*pointer来调用value receiver也就是valueMethod
但是value变量就不行。
因为存储在interface里的值是不能取地址的,所以调用不了pointerMethod
总的来说当一个接口I有value receiver和pointer receiver时,指针变量(*T)是满足这个接口的
但是值类型的变量(T)就不行,因为T赋值给I后,不能取地址
解决问题
经过上面的解释,我们现在自然的找到了解决我一开始遇到的问题的方法
这里有两种解决方法
-
把
s的类型改为指针1 2 3 4 5 6 7 8 9 10 11 12type str string func (s *str) MarshalJSON() (data []byte, err error) { return json.Marshal("bbb") } func TestStr(t *testing.T) { temp := "aaa" var s *str = (*str)(&temp) //改为指针 bytes, _ := json.Marshal(s) fmt.Println(string(bytes)) } -
从pointer receiver改为value receiver
1 2 3 4 5 6 7 8 9 10 11type str string func (s str) MarshalJSON() (data []byte, err error) { //把 * 去掉 return json.Marshal("bbb") } func TestStr(t *testing.T) { var s str = "aaa" bytes, _ := json.Marshal(s) fmt.Println(string(bytes)) }
参考
-
https://stackoverflow.com/questions/33587227/golang-method-sets-pointer-vs-value-receiver
-
https://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
-
http://shanks.leanote.com/post/Untitled-55ca439338f41148cd000759-17
-
https://sanyuesha.com/2017/07/22/how-to-understand-go-interface/
-
https://tonybai.com/2015/09/17/7-things-you-may-not-pay-attation-to-in-go/