go结构体中的匿名变量在json.marshal中隐藏的坑

故障背景

在昨天的工作中,遇到一个诡异的小问题,调试了一段时间,在网上也没有找到相关材料(可能谷歌能力有限,搜索不到,要用百度)。先来看一段简单的简化后的代码,简单、清晰、明了、接地气,与我的工作场景一致。

/*   * Copyright (c) 2020.   * [email protected]   */    package main    import (     "encoding/json"     "fmt"  )    type L5 struct {     Modid int64 `json:"modid"`     Cmdid int64 `json:"cmdid"`  }    type CommonAttr struct {     Name string `json:"name"`     Desc string `json:"desc"`  }    type OperationQueryCKV struct {     L5     Expression string `json:"expression"`     TimeoutMs  int64  `json:"timeout_ms"`  }    type Operation struct {     CommonAttr     *OperationQueryCKV  }    func main() {     op := &Operation{        CommonAttr{           Name: "op_name",           Desc: "op_desc",        },        &OperationQueryCKV{           L5: L5{              Modid: 12345,              Cmdid: 54321,           },           Expression: "expression",           TimeoutMs:  80,        },     }     b, _ := json.MarshalIndent(op,"","t")     fmt.Printf("%sn", string(b))  }

关注一下L5结构,这是一种公司内部服务负载均衡和服务发现的公共件,简单的说,一个L5可以换到一个IP和端口列表,然后拿去访问服务。对于CKV查询功能来说,需要一个L5表明CKV在哪。

漫天飞舞的匿名字段,是不是像golang大神特有的标签。运行一下,没问题,符合预期,完美。

[[email protected]_15_146_centos ~]# go run main.go  {          "name": "op_name",          "desc": "op_desc",          "modid": 12345,          "cmdid": 54321,          "expression": "expression",          "timeout_ms": 80  }

故障现象

有一天,这样跑了一年的代码突然就不对了,测试发现,OperationQueryCKV结构序列化之后modid和cmdid都消失了,很邪乎。告诉我这个问题后,我也是一脸懵,我最近没有改动相关代码呀。都跑了一年了你跟我说这东西不行了?

故障原因

查看代码提交记录,一处改动进入视野,最近新增了一个查询数据库的功能,与OperationQueryCKV类似的,新增定义了一个OperationQuerySQL,只是新定义了结构,还没有实现和严格测试。

type OperationQuerySQL struct {     L5     User string `json:"user"`     Pwd  string `json:"pwd"`  }    type Operation struct {     CommonAttr     *OperationQueryCKV     *OperationQuerySQL  }

查询数据库也需要一个L5,一贯风格,新增一个L5的匿名变量,完活。估计就是两个匿名L5,把值搞没了。试着跑一下样例代码。

func main() {     op := &Operation{        CommonAttr{           Name: "op_name",           Desc: "op_desc",        },        &OperationQueryCKV{           L5: L5{              Modid: 12345,              Cmdid: 54321,           },           Expression: "expression",           TimeoutMs:  80,        },        nil,     }     b, _ := json.MarshalIndent(op, "", "t")     fmt.Printf("%sn", string(b))  }    

结果,我的L5果然没有了。根本原因应该是匿名变量导致的json字段key重复。

{          "name": "op_name",          "desc": "op_desc",          "expression": "expression",          "timeout_ms": 80  }

解决方法

这样的坑,在分工合作时会不经意引入,有时比较隐晦。

暂时也不知道好的办法,建议是不用或者少用匿名字段,如果需要使用匿名字段,每项都定义不同的`json:"_key_“`。