yaml解析踩坑实录——是谁吃掉了我的匿名变量

flappybird · · 1329 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文记录了本人在做yaml解析时踩的坑,谨以此纪念我在这几个小时里挠掉的头发。

情景再现

昨天发现之前改的一个yaml文件解析出了问题:我需要从一个yaml配置文件里读取内容,然后解析到一个struct中。但实际运行时发现,有一部分参数没有解析出来,是空的。 看看代码(注:以下都是把我的实际问题做了抽象和简化之后的代码):

我的yaml配置文件:

kind: PersonalInfo
name: he
age: 18
复制代码

接收配置的结构体定义:

type Config struct {
  TypeMeta `json:",inline"`
  Name     string `json:"name"`
  Words    string `json:"words"` 
}

type TypeMeta struct {
   Kind string `json:"kind,omitempty"` 
}
复制代码

刚开始我是直接用了go-yaml包,这样做的解析:

func TestParseYaml(t *testing.T) {
   ymlFilePath := "conf.yaml"    
   ymlFile, err := os.Open(ymlFilePath)
   if err != nil {
      t.Errorf("open yaml file(%s) failed: %v", ymlFilePath, err)
      return
  }
   defer ymlFile.Close()

   ymlContent, err := ioutil.ReadAll(ymlFile)
   if err != nil {
      t.Errorf("read yaml file(%s) failed: %v", ymlFilePath, err)
      return
  }

  var config Config
  err = yaml.Unmarshal(ymlContent, &config)
   if err != nil {
      t.Errorf("yaml format error: %v", err)
      return
  }

   t.Logf("%#v", config)
}
复制代码

运行输出结果为:

{TypeMeta:{Kind:} Name:he Age:18}
复制代码

看着这个解析结果,我不禁陷入了沉思——我的kind呢???是谁吃掉了我的kind???

原因分析

我尝试修改了我的struct,再测试了几遍,发现没有解析出来的都是在匿名变量中的数据。

但是我的struct去解析json字符串的话,所有字段,包括匿名变量中的字段,全部都能正常解析。

到底是什么影响了我解析yaml格式的数据呢?

找了一堆资料进行了一通分析,终于发现了华点——struct的标签一直被我忽略掉了!

我的struct里面,都只加了json的标签,并没有任何yaml相关的标签,所以在进行yaml转换的时候,相当于所有字段都没有打标签。匿名成员如果没有加inline标签,在解析时是会被忽略的。

修改验证

基于以上分析,我修改了我的struct类型定义,给匿名变量加上了yaml的inline标签:

type Config struct {
  TypeMeta `json:",inline" yaml:",inline"`
  Name     string `json:"name"`
  Age      int `json:"age"` 
}

type TypeMeta struct {
   Kind string `json:"kind,omitempty"` 
}
复制代码

再次执行我的解析函数,得到的结果如下:

{TypeMeta:{Kind:PersonalInfo} Name:he Age:18}
复制代码

泪目,我终于拿到了正确的结果!

最终方案

虽然在struct上添加yaml标签就可以得到想要的输出,但是看看我们的代码,struct上加的标签几乎全都是json的,大家并不会单独给yaml加标签。 如果我单独给我用到的这个struct加yaml标签,好像有点不符合我们一贯的风格。而且大家定义struct时几乎都不会给变量加yaml标签,下次要是别的谁又加了个匿名成员,却忘记加yaml标签,岂不就凉凉了?

所以,经过一番权衡,我决定先把从配置文件读取到的yaml内容转换成json,然后再使用json的unmarshal方法,把json转换为struct。

最终解析yaml的方法如下:

type Config struct {
  TypeMeta `json:",inline"`
  Name     string `json:"name"`
  Age      int `json:"age"` 
}

type TypeMeta struct {
   Kind string `json:"kind,omitempty"` 
}


func TestParseYaml(t *testing.T) {
   ymlFilePath := "conf.yaml"    
   ymlFile, err := os.Open(ymlFilePath)
   if err != nil {
      t.Errorf("open yaml file(%s) failed: %v", ymlFilePath, err)
      return
  }
   defer ymlFile.Close()
   ymlContent, err := ioutil.ReadAll(ymlFile)
   if err != nil {
      t.Errorf("read yaml file(%s) failed: %v", ymlFilePath, err)
      return
  }
   data, err := yaml.ToJSON(ymlContent)

   var config Config
  err = json.Unmarshal(data, &config)
   if err != nil {
      t.Errorf("yaml format error: %v", err)
      return
  }

   t.Logf("%+v", config)
}
复制代码

输出结果:

{TypeMeta:{Kind:PersonalInfo} Name:he Age:18}复制代码

有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:flappybird

查看原文:yaml解析踩坑实录——是谁吃掉了我的匿名变量

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1329 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传