yaml парсинг пит-записи — кто съел мою анонимную переменную

Go

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