云原生相關(guān)的項(xiàng)目避免不了和 yaml 打交道草添。go 沒有內(nèi)置對 yaml 格式的支持,比較常用的 go-yaml 這個庫智亮,這個庫可以用進(jìn)行日常的 marshal 和 unmarshal赞哗。
k8s 自身也常和 yaml 打交道牌里,所以 k8s 提供了一個 sigs.k8s.io/yaml 庫融柬。這個庫自身也是基于 go-yaml,所以文檔中也說明了 k8syaml 支持 go-yaml 的全部功能趋距。
我們在一個項(xiàng)目中一開始引入的是 go-yaml 的依賴粒氧,后來引入了 k8syaml 依賴,導(dǎo)致在代碼的不同模塊中分別使用兩個模塊進(jìn)行序列化和反序列化节腐,最終觸發(fā)了一個系統(tǒng) bug外盯,該 bug 的表現(xiàn)一個配置項(xiàng)原本沒有發(fā)生變化,但是系統(tǒng)會顯示為發(fā)生了變化翼雀。經(jīng)過 debug 才發(fā)現(xiàn)是由于在舊值和新值分別使用了 go-yaml 和 k8s yaml 進(jìn)行了初始化導(dǎo)致的饱苟。
我們寫一小段代碼進(jìn)行驗(yàn)證一下,發(fā)現(xiàn)由于 k8syaml 會先將 data 轉(zhuǎn)為 json 再 unmarshal狼渊,導(dǎo)致 Value 的類型為 float64箱熬,而直接使用 go-yaml 中話类垦,Value 的類型則是 int。
k8s-yaml Unmarshal 出來的類型之所以為 float64城须,也不是做了什么魔法蚤认,僅僅是先 unmarshal 到 json 導(dǎo)致的,我們直接使用 json unmarshal 的話糕伐,Value 的類型也是 float64砰琢。不過 json 提供了 decode 提供了選項(xiàng) UseNumger()
的選項(xiàng)來處理此行為。
其實(shí) k8syaml 的這個行為在文檔中也有明確說明良瞧,只是之前沒有觸碰到這個先轉(zhuǎn)為 json 再轉(zhuǎn)為 yaml 的問題陪汽,算是踩了一次坑。
package main
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"gopkg.in/yaml.v3"
k8syaml "sigs.k8s.io/yaml"
)
func MustUnmarshalJSON(data []byte, o interface{}, useNumber bool) {
var dec = json.NewDecoder(bytes.NewReader(data))
if useNumber {
dec.UseNumber()
}
err := dec.Decode(o)
if err != nil {
panic(err)
}
}
const (
DefaultYaml = iota
K8SYaml
)
func MustUnmarshalYAML(data []byte, o interface{}, yamlType int, opts ...k8syaml.JSONOpt) {
var err error
switch yamlType {
case DefaultYaml:
err = yaml.Unmarshal(data, o)
case K8SYaml:
err = k8syaml.Unmarshal(data, o, opts...)
default:
panic(fmt.Errorf("unknown yamlType %v", yamlType))
}
if err != nil {
panic(err)
}
}
var useNumberOpt = func(d *json.Decoder) *json.Decoder {
d.UseNumber()
return d
}
func main() {
type data struct {
yaml string
json string
}
type dd struct {
Value interface{} `yaml:"value"`
}
for _, o := range []data{
{
yaml: `value: 7`,
json: `{"value": 7}`,
},
{
yaml: `value: 7.4`,
json: `{"value":7.4}`,
},
} {
var d dd
fmt.Printf("Input:%+v\n", o)
MustUnmarshalJSON([]byte(o.json), &d, false)
fmt.Printf("JSON Unmarshal Default: %+v, type of value: %v\n", d, reflect.TypeOf(d.Value))
MustUnmarshalJSON([]byte(o.json), &d, true)
fmt.Printf("JSON Unmarshal UseNumber: %+v, type of value: %v\n", d, reflect.TypeOf(d.Value))
MustUnmarshalYAML([]byte(o.yaml), &d, DefaultYaml)
fmt.Printf("Default YAML Unmarshal %+v, type of value: %v\n", d, reflect.TypeOf(d.Value))
MustUnmarshalYAML([]byte(o.yaml), &d, K8SYaml)
fmt.Printf("K8SYAML Unmarshal %+v, type of value: %v\n", d, reflect.TypeOf(d.Value))
MustUnmarshalYAML([]byte(o.yaml), &d, K8SYaml, useNumberOpt)
fmt.Printf("K8SYAML Unmarshal With JSONOpt %+v, type of value: %v\n", d, reflect.TypeOf(d.Value))
}
}