golang json自定义解析

golang json自定义解析

1.使用背景

假设json格式为:

{
 "op": "+",
 "num1": 10,
 "num2": 13
}

最终需要转换为:

{
 "op": "+",
 "result": 23
}

此时,golang的json的序列化是否支持呢?

2.自定义解析

golang encoding/json中支持用户自定义json序列化,只需要实现当前结构体的UnmarshalJSON/MarshalJSON。

例如:上述例子,这里以UnmarshalJSON为例。

// UnmarshalJSON
func (o *Operation) UnmarshalJSON(data []byte) error {
 // type 定义新类型  避免内部循环引用 导致stack overflow
 type opShadow Operation
 var tmp opShadow
 if err := json.Unmarshal([]byte(data), &tmp); err != nil {
  return err
 }
 switch tmp.Op {
 case "+":
  o.Result = tmp.Num1 + tmp.Num2
 case "-":
  o.Result = tmp.Num1 - tmp.Num2
 }
 o.Op = tmp.Op
 return nil
}

实际上就是在方法中实现自己的业务逻辑,注意内部使用了Unmarshal,会导致鸡生蛋,蛋生鸡的问题,谁依赖谁,一直死循环依赖,最终导致stack overflow,因此在内部需要type一个类型,用这个别名类型来操作。

3.使用编译时断言

在上述使用代码中,我们添加:

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

便可以在编译时看到自己写的UnmarshalJSON接口是否正确,如果没写/不正确,此时会报错,非常好用。

在c++中我们也是秉持编译时优先抛出问题,golang也是如此,使用编译时的断言简单好用,还可以避免一些错误问题。

4.嵌入式结构体

针对嵌入式结构体的UnmarshalJSON使用,我们往往会出错,例如:

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

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

func (n *Nested) UnmarshalJSON(data []byte) error {
 *n = Nested{}
 tmp := struct {
  Dur string `json:"duration"`
 }{}
 fmt.Printf("parsing nested json %s \n", string(data))
 if err := json.Unmarshal(data, &tmp); err != nil {
  fmt.Printf("failed to parse nested: %v", err)
  return err
 }
 tmpDur, err := time.ParseDuration(tmp.Dur)
 if err != nil {
  fmt.Printf("failed to parse duration: %v", err)
  return err
 }
 (*n).Dur = tmpDur
 return nil
}

type Object struct {
 Nested
 Num int `json:"num"`
}
func (o *Object) UnmarshalJSON(data []byte) error {
 *o = Object{}
 tmp := struct {
  Nested
  Num int `json:"num"`
 }{}
 fmt.Printf("parsing object json %s \n", string(data))
 if err := json.Unmarshal(data, &tmp); err != nil {
  fmt.Printf("failed to parse object: %v", err)
  return err
 }
 fmt.Printf("tmp object: %+v \n", tmp)
 (*o).Num = tmp.Num
 (*o).Nested = tmp.Nested
 return nil
}

在这段代码中,我们最终想要做的是Unmarshal为Object,那么会调用UnmarshalJSON,在这个方法中,调用了Unmarshal,此时会去调用Nested的UnmarshalJSON,便会导致num解析不出来,为了解决这种问题,我们可以做两次解析,也就是潜入类型与本身成员拆分。

tmp := struct {
  //Nested
  Num int `json:"num"`

}{}
// unmarshal Nested alone
tmpNest := struct {
  Nested
}{}
fmt.Printf("parsing object json %s \n", string(data))
if err := json.Unmarshal(data, &tmp); err != nil {
  fmt.Printf("failed to parse object: %v", err)
  return err
}
// the Nested impl UnmarshalJSON, so it should be unmarshaled alone
if err := json.Unmarshal(data, &tmpNest); err != nil {
  fmt.Printf("failed to parse object: %v", err)
  return err
}

至此,便可以解决嵌入类型Unmarshal的问题。

本节完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值