20

promoted Unmarshal method on embedded field caused confusion 后续

 3 years ago
source link: https://studygolang.com/articles/29217
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
encoding/json: promoted Unmarshal method on embedded field caused confusion

接上一篇,官方在早上给了回复:

cac43ceaab737e59849e32d9ea8af75e.jpg

简单解释下就是嵌入字段 Nested 的方法被提升了,导致 Object 的方法不会被执行,所以 Num 字段不会被 Unmarshal 。跟上一篇中的解释差不多意思。但是官方给了两种更加优雅的解决这个问题的方式,让我们来欣赏下大佬的代码。

方法 a

代码中只需添加下面一行即可:

var _ json.Unmarshaler = (*Object)(nil)
复制代码
package main

import (
 "encoding/json"
 "fmt"
 "time"
)

var testJSON = `{"num":5,"duration":"5s"}`

type Nested struct {
 Dur time.Duration `json:"duration"`
}

func (obj *Object) UnmarshalJSON(data []byte) error {
 tmp := struct {
  Dur string `json:"duration"`
  Num int    `json:"num"`
 }{}
 if err := json.Unmarshal(data, &tmp); err != nil {
  return err
 }

 dur, err := time.ParseDuration(tmp.Dur)
 if err != nil {
  return err
 }
 obj.Dur = dur
 obj.Num = tmp.Num
 return nil
}

type Object struct {
 Nested
 Num int `json:"num"`
}

var _ json.Unmarshaler = (*Object)(nil)

func main() {
 obj := Object{}
 _ = json.Unmarshal([]byte(testJSON), &obj)
 fmt.Printf("result: %+v \n", obj)
}
复制代码

随后这位老哥补充到,在嵌入字段都实现了接口方法的情况下, The type assertion will be a nice guide , 添加该类型的断言是一个好的实践,可以帮助你快速捕捉到潜在的 bug

方法 b

实现 custom time unmarshaller

package main

import (
 "encoding/json"
 "fmt"
 "time"
)

var testJSON = `{"num":5,"duration":"5s"}`

type customTimeDuration time.Duration

type Nested struct {
 Dur customTimeDuration `json:"duration"`
}

func (ctd *customTimeDuration) UnmarshalJSON(b []byte) error {
 var durStr string
 if err := json.Unmarshal(b, &durStr); err != nil {
  return err
 }
 dur, err := time.ParseDuration(durStr)
 if err == nil {
  *ctd = customTimeDuration(dur)
 }
 return err
}

type Object struct {
 Nested
 Num int `json:"num"`
}

func main() {
 obj := Object{}
 _ = json.Unmarshal([]byte(testJSON), &obj)
 fmt.Printf("result: %+v \n", obj)
}
复制代码

这种方式其实就是跟以上一篇分开解析的思路比较像,他重新声明了别名类型,然后为这个别名类型实现 UnmarshalJson 接口。个人倾向于第一种添加类型断言的方式,简洁又容易理解 ,对代码侵入比较小。

官方对这个问题的回复还是很热情的,他说他自己的团队在几年前也遇到了一模一样的问题,很能理解开发者的心情,他当时还针对这个问题写了一篇类似的文章,https://medium.com/@odeke_et/compile-type-assertions-to-the-rescue-6ddab4b8398b。我说啥来着,这是一个前人踩坑,后人踩坑,未来还会踩的坑。

u1s1, 这位大佬给出的方案和代码还是很赏心悦目的,值得学习(抄一下)。

------------------------------ END ----------------------------------


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK