В этой статье описывается яма, на которую я наступил, когда занимался разбором 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}