В этой статье рассматриваются некоторые проблемы и решения, с которыми мы часто сталкиваемся в проектах по преобразованию между данными 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"`
- Тег структуры состоит из одной или нескольких пар ключ-значение. Ключи и значения разделяются двоеточиями, а значения заключаются в двойные кавычки.
- Одно и то же поле структуры может устанавливать несколько тегов пары ключ-значение и использовать пробелы для разделения разных пар ключ-значение.
Используйте тег 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, я приготовил для вас подарок.