Советы по использованию пакета json для языка Go |Go Theme Month

Go

В этой статье рассматриваются некоторые проблемы и решения, с которыми мы часто сталкиваемся в проектах по преобразованию между данными JSON языка go и структурами.

базовая сериализация

Во-первых, давайте рассмотрим базовое использование json.Marshal (сериализация) и json.Unmarshal (десериализация) в языке Go.

type Person struct {
	Name   string
	Age    int64
	Weight float64
}

func main() {
	p1 := Person{
		Name:   "小明",
		Age:    18,
		Weight: 71.5,
	}
	// struct -> json string
	b, err := json.Marshal(p1)
	if err != nil {
		fmt.Printf("json.Marshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
	// json string -> struct
	var p2 Person
	err = json.Unmarshal(b, &p2)
	if err != nil {
		fmt.Printf("json.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("p2:%#v\n", p2)
}

выход:

str:{"Name":"小明","Age":18,"Weight":71.5}
p2:main.Person{Name:"小明", Age:18, Weight:71.5}

Введение тега структуры

Тег — это метаинформация структуры, которую можно прочитать во время выполнения с помощью механизма отражения. Тег определяется в конце поля структуры и оборачивается парой обратных кавычек. Конкретный формат выглядит следующим образом:

`key1:"value1" key2:"value2"`
  1. Тег структуры состоит из одной или нескольких пар ключ-значение. Ключи и значения разделяются двоеточиями, а значения заключаются в двойные кавычки.
  2. Одно и то же поле структуры может устанавливать несколько тегов пары ключ-значение и использовать пробелы для разделения разных пар ключ-значение.

Используйте тег json, чтобы указать имя поля

Сериализация и десериализация используют имя поля структуры по умолчанию.Мы можем указать имя поля, сгенерированное сериализацией json, добавив тег в поле структуры.

// 使用json tag指定序列化与反序列化时的行为
type Person struct {
	Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
	Age    int64
	Weight float64
}

игнорировать поле

Если вы хотите игнорировать поле в структуре во время сериализации/десериализации json, вы можете добавить - к тегу следующим образом.

// 使用json tag指定json序列化与反序列化时的行为
type Person struct {
	Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
	Age    int64
	Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
}

Игнорировать пустые поля

Когда поля в структуре не имеют значения, json.Marshal() не будет игнорировать эти поля во время сериализации, но по умолчанию будет использовать нулевое значение типа выходного поля (например, нулевое значение типов int и float равно 0, и нулевое значение строкового типа равно "" ", нулевое значение типа объекта равно nil). Если вы хотите игнорировать эти поля без значения во время сериализации, вы можете добавить тег omitempty к соответствующему полю.

Например:

type User struct {
	Name  string   `json:"name"`
	Email string   `json:"email"`
	Hobby []string `json:"hobby"`
}

func omitemptyDemo() {
	u1 := User{
		Name: "小明",
	}
	// struct -> json string
	b, err := json.Marshal(u1)
	if err != nil {
		fmt.Printf("json.Marshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
}

Выходной результат:

str:{"name":"小明","email":"","hobby":null}

Если вы хотите удалить нулевое поле из окончательного результата сериализации, вы можете определить структуру следующим образом:

// 在tag中添加omitempty忽略空值
// 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
type User struct {
	Name  string   `json:"name"`
	Email string   `json:"email,omitempty"`
	Hobby []string `json:"hobby,omitempty"`
}

В этот момент снова запустите приведенный выше omitemptyDemo, и результат будет следующим:

str:{"name":"小明"} // 序列化结果中没有email和hobby字段

В качестве отступления, когда мы используем gorm для работы с базой данных, мы часто сталкиваемся с проблемой игнорирования модификации указанных полей, таких как связанные сущности в структуре, желание отображать только json и игнорирование сущностей при отправке формы. разберемся с этой проблемой отдельно.

Игнорировать пустые поля вложенной структуры

Давайте сначала рассмотрим несколько примеров вложенных структур:

type User struct {
	Name  string   `json:"name"`
	Email string   `json:"email,omitempty"`
	Hobby []string `json:"hobby,omitempty"`
	Profile
}

type Profile struct {
	Website string `json:"site"`
	Slogan  string `json:"slogan"`
}

func nestedStructDemo() {
	u1 := User{
		Name:  "小明",
		Hobby: []string{"足球", "篮球"},
	}
	b, err := json.Marshal(u1)
	if err != nil {
		fmt.Printf("json.Marshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)
}

При анонимном вложении профиля сериализованная строка json является одноуровневой:

str:{"name":"小明","hobby":["足球","蓝球"],"site":"","slogan":""}

Чтобы стать вложенной строкой json, вам нужно перейти на именованное вложение или определить тег поля:

type User struct {
	Name    string   `json:"name"`
	Email   string   `json:"email,omitempty"`
	Hobby   []string `json:"hobby,omitempty"`
	Profile `json:"profile"`
}
// str:{"name":"小明","hobby":["足球","篮球"],"profile":{"site":"","slogan":""}}
想要在嵌套的结构体为空值时,忽略该字段,仅添加omitempty是不够的:

type User struct {
	Name     string   `json:"name"`
	Email    string   `json:"email,omitempty"`
	Hobby    []string `json:"hobby,omitempty"`
	Profile `json:"profile,omitempty"`
}
// str:{"name":"小明","hobby":["足球","篮球"],"profile":{"site":"","slogan":""}}

Вам также необходимо использовать вложенные указатели на структуры:

type User struct {
	Name     string   `json:"name"`
	Email    string   `json:"email,omitempty"`
	Hobby    []string `json:"hobby,omitempty"`
	*Profile `json:"profile,omitempty"`  //这里是重点
}
// str:{"name":"小明","hobby":["足球","篮球"]}

Не изменяйте исходную структуру, чтобы игнорировать нулевые поля.

Нам нужен json для сериализации пользователя, но мы не хотим сериализовать пароль, и мы не хотим изменять структуру пользователя.На данный момент мы можем создать другую структуру, PublicUser, для анонимного вложения исходного пользователя и укажите в поле Password тип указателя анонимной структуры и добавьте omitemptytag, пример кода выглядит следующим образом:

type User struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type PublicUser struct {
	*User             // 匿名嵌套
	Password *struct{} `json:"password,omitempty"`
}

func omitPasswordDemo() {
	u1 := User{
		Name:     "小明",
		Password: "123456",
	}
	b, err := json.Marshal(PublicUser{User: &u1})
	if err != nil {
		fmt.Printf("json.Marshal u1 failed, err:%v\n", err)
		return
	}
	fmt.Printf("str:%s\n", b)  // str:{"name":"小明"}
}

Изящно обрабатывать числа в строковом формате

Иногда интерфейс может использовать числа строкового типа в передаваемых данных json.В настоящее время вы можете добавить строку в тег структуры, чтобы указать пакету json проанализировать данные соответствующего поля из строки:

type Card struct {
	ID    int64   `json:"id,string"`    // 添加string tag
	Score float64 `json:"score,string"` // 添加string tag
}

func intAndStringDemo() {
	jsonStr1 := `{"id": "1234567","score": "88.50"}`
	var c1 Card
	if err := json.Unmarshal([]byte(jsonStr1), &c1); err != nil {
		fmt.Printf("json.Unmarsha jsonStr1 failed, err:%v\n", err)
		return
	}
	fmt.Printf("c1:%#v\n", c1) // c1:main.Card{ID:1234567, Score:88.5}
}

Суммировать

Сегодня просто разобрался с некоторыми навыками использования json.В реальных проектах json незаменимая часть.Сегодня поставил флаг,а в следующей статье разберу навыки использования,связанные с gorm.

обо мне

Я полон решимости стать полноценным One Piece, добро пожаловать в мое морское путешествие, найдите «Wang Zhongyang» в общедоступном аккаунте WeChat, я приготовил для вас подарок.