Анализ структурных тегов

Отрывок из Эффективных рецептов го Мики Тебека

Задача

Вы создаете ORM (объектно-реляционный преобразователь) для собственной базы данных вашей компании. ORM упрощает работу с базой данных; вы сохраняете и извлекаете структуры Go напрямую, вместо использования []any.

Ваша первая задача — проанализировать пользовательскую структуру и вернуть соответствие между именами полей и столбцами базы данных. Например, для следующей структуры:

// Log is a log structure

type Log struct {
    Time time.Time     `db:"ts"` 
    Level int          `db:"level"` 
   Text string         `db:"message"`
}

Вы должны вернуть отображение:

  • Время → тс
  • Уровень → уровень
  • Текст → сообщение

Решение

Вы начинаете с определения сигнатуры функции синтаксического анализа:

func parseStructTags(s any) (map[string]string, error) {

Сначала вы используете пакет отражения, чтобы получить тип параметра:

typ := reflect.TypeOf(s)
if typ.Kind() != reflect.Struct {
    return nil, fmt.Errorf("%s is not a struct", typ)
}

Затем вы перебираете поля структуры и извлекаете ключ db из тега поля:

m := make(map[string]string)
for i := 0; i < typ.NumField(); i++ {
    fld := typ.Field(i)
    if dbName := fld.Tag.Get("db"); dbName != "" { 
        m[fld.Name] = dbName
    }
}
return m, nil

Обсуждение

Поскольку parseStructTags должен принимать любое значение, тип его параметра — any. В большинстве случаев использование any является запашком кода, и вам следует использовать конкретные типы или интерфейс.

Однако в этом случае вы не можете использовать конкретный тип или интерфейс, поэтому вам остается использовать any.

📝 Примечание. Термин запах кода был придуман Кентом Беком и означает поверхностный признак, который обычно соответствует более глубокой проблеме в системе.

Теги структур используются многими пакетами сериализации; их используют встроенные encoding/json, encoding/xml и многие другие внешние пакеты, такие как yaml.

Структурные теги позволяют добавлять дополнительную информацию о поле. Они имеют известный формат: key:”value” key:”value” … Тип Field в пакете reflect имеет методы для извлечения определенного ключа.

В некоторых случаях значение может быть больше, чем просто имя. Обычно это список значений, разделенных , или name=value. Вот пример структуры, созданной инструментом Google protoc:

Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"`

Тег структуры имеет два ключа: protobuf и json, и оба имеют комплексные значения.

Если вы решите, что вам нужны сложные значения (например, protobuf в нашем примере), вам необходимо разработать формат, используемый в этих тегах, поскольку вы по сути реализуете свой собственный формат сериализации.

Лучше копировать то, что делают encoding/json или protobuf, а не изобретать что-то свое.

Мы надеемся, что вам понравился этот отрывок из Эффективных рецептов го Мики Тебеки. Книга сейчас находится в бета-версии от The Pragmatic Bookshelf:



Нам бы хотелось услышать ваше мнение, поэтому, если вы получите бета-версию, поделитесь комментариями и предложениями на странице книги на DevTalk.