В этой статье описывается яма, на которую я наступил, когда занимался разбором yaml, чтобы почтить память волос, которые я почесал за эти несколько часов.
Реконструкция сцены
Вчера я обнаружил проблему с разбором файла yaml, который я изменил ранее: мне нужно прочитать содержимое из файла конфигурации yaml, а затем разобрать его в структуру. Однако в ходе фактической работы было обнаружено, что некоторые параметры не анализировались и были пустыми. Взгляните на код (примечание: ниже приведен код после абстрагирования и упрощения моей реальной проблемы):
Мой файл конфигурации 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}
Глядя на результаты этого анализа, я не мог не задуматься – а как же мой вид? ? ? кто съел мой вид? ? ?
Анализ причин
Я попытался изменить свою структуру и протестировал ее несколько раз и обнаружил, что данные, которые не были проанализированы, были данными в анонимной переменной.
Но если моя структура анализирует строку json, все поля, в том числе в анонимных переменных, могут быть проанализированы нормально.
Что влияет на мой разбор данных в формате yaml?
Перерыв кучу информации для анализа, я наконец нашел Huadian — метка struct мной все время игнорировалась!
В моей структуре добавляются только теги json, а теги, связанные с yaml, отсутствуют, поэтому при выполнении преобразования yaml это эквивалентно тому, что все поля не помечены. Анонимные участники, если они не добавленыinline
теги, которые игнорируются при разборе.
Изменить проверку
Основываясь на приведенном выше анализе, я изменил определение типа структуры и добавил встроенный тег yaml в анонимную переменную:
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}
Слезы, наконец-то я получил правильный результат!
Окончательное предложение
Хотя вы можете получить желаемый результат, добавив теги yaml в структуру, но, глядя на наш код, почти все теги, добавленные в структуру, являются json, и вы не добавляете теги только в yaml. Если я добавлю тег yaml в структуру, которую использую сам по себе, это покажется немного не соответствующим нашему обычному стилю. И когда вы определяете структуру, вы почти никогда не добавляете теги yaml к переменным.В следующий раз, если кто-то еще добавит анонимный член, но забудет добавить теги yaml, разве это не круто?
Итак, после некоторого взвешивания я решил сначала преобразовать содержимое yaml, считанное из файла конфигурации, в json, а затем использовать метод unmarshal json для преобразования json в структуру.
Последний способ разбора 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}