Golang中如何实现仅忽略字段的Marshal但不忽略Unmarshal
Golang中如何实现仅忽略字段的Marshal但不忽略Unmarshal
我正在使用Go API来访问我的SQL表。我的所有表都有created_at和updated_at字段,我希望这些字段由SQL控制,而不是Go。例如:
query, err := GetObjects[SomeSchema]("table_name") // 调用表,然后反序列化到 []SomeSchema
assert.Equal(query[0], SomeSchema{id: "whatever", created_at: /* 某个时间对象 */, updated_at: ... })
我希望找到一种简单的方法,在序列化时忽略所有此类模式对象中的created_at和updated_at字段,这样它们就不会覆盖SQL中的值,但在反序列化时它们会正常读取。
我曾希望嵌入一个包含两个*time.Time对象的结构体可以解决这个问题,使其在序列化时忽略这些字段,但遗憾的是,MarshalJSON()重写会被提升到父对象。据我理解,我知道的最简单的方法是,为我的10多个结构体中的每一个编写一个自定义的MarshalJSON(),但这并不容易,因为我有大量的对象。代码看起来像这样:
func (s SomeSchema) MarshalJSON() ([]byte, error) {
type SomeSchemaWithoutThoseFields struct {
Id string `json:"id"
}
return json.Marshal(SomeSchemaWithoutThoseFields{id: s.Id})
}
有没有更简单、更符合Go语言习惯的方法来实现这个?
更多关于Golang中如何实现仅忽略字段的Marshal但不忽略Unmarshal的实战教程也可以访问 https://www.itying.com/category-94-b0.html
实现此目标的一种方法是使用结构体组合,并嵌入包含您希望在JSON编组中排除的字段的结构体。以下是一个示例:
type Timestamps struct {
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
}
type SomeSchema struct {
Timestamps
ID string `json:"id"`
// 其他字段...
}
在这种方法中,Timestamps 被嵌入到 SomeSchema 中,但字段 CreatedAt 和 UpdatedAt 被标记为 json:"-",这向JSON编组器指示在编组过程中应忽略这些字段。
这样一来,您无需为每个结构体编写自定义的 MarshalJSON() 方法,这些字段在编组时将被忽略,而在解组时仍能正常读取。
更多关于Golang中如何实现仅忽略字段的Marshal但不忽略Unmarshal的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中实现仅忽略字段的Marshal但不忽略Unmarshal,可以使用结构体嵌入结合json标签的-标记和指针类型。以下是几种实现方式:
方法1:使用组合结构体(推荐)
type Timestamps struct {
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type SomeSchema struct {
ID string `json:"id"`
Timestamps
}
// MarshalJSON 重写,忽略Timestamps字段
func (s SomeSchema) MarshalJSON() ([]byte, error) {
type Alias SomeSchema
return json.Marshal(&struct {
*Alias
Timestamps *struct{} `json:"timestamps,omitempty"`
}{
Alias: (*Alias)(&s),
})
}
方法2:使用接口和自定义Marshaler
type Timestamped interface {
GetTimestamps() Timestamps
}
type BaseModel struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (b BaseModel) GetTimestamps() Timestamps {
return Timestamps{
CreatedAt: &b.CreatedAt,
UpdatedAt: &b.UpdatedAt,
}
}
type SomeSchema struct {
ID string `json:"id"`
BaseModel
}
// 为所有需要此功能的结构体实现MarshalJSON
func (s SomeSchema) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
ID string `json:"id"`
}{
ID: s.ID,
})
}
方法3:使用反射生成MarshalJSON(适用于多个结构体)
func GenerateMarshalJSON(skipFields ...string) func(interface{}) ([]byte, error) {
return func(v interface{}) ([]byte, error) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
result := make(map[string]interface{})
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Tag.Get("json")
if fieldName == "" {
fieldName = field.Name
}
// 跳过指定的字段
skip := false
for _, skipField := range skipFields {
if strings.Split(fieldName, ",")[0] == skipField {
skip = true
break
}
}
if !skip {
result[fieldName] = val.Field(i).Interface()
}
}
return json.Marshal(result)
}
}
// 使用方式
type SomeSchema struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (s SomeSchema) MarshalJSON() ([]byte, error) {
marshal := GenerateMarshalJSON("created_at", "updated_at")
return marshal(s)
}
方法4:使用匿名结构体包装器
type MarshalExcluder struct{}
func (m MarshalExcluder) ExcludeTimestamps(v interface{}) ([]byte, error) {
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
var obj map[string]interface{}
if err := json.Unmarshal(data, &obj); err != nil {
return nil, err
}
delete(obj, "created_at")
delete(obj, "updated_at")
return json.Marshal(obj)
}
// 使用方式
type SomeSchema struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (s SomeSchema) MarshalJSON() ([]byte, error) {
excluder := MarshalExcluder{}
return excluder.ExcludeTimestamps(s)
}
方法5:使用代码生成工具
对于大量结构体,可以考虑使用go generate和模板生成MarshalJSON方法:
//go:generate go run generate_marshal.go
// generate_marshal.go
package main
import (
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"text/template"
)
const marshalTemplate = `
func ({{.Receiver}} {{.TypeName}}) MarshalJSON() ([]byte, error) {
type Alias {{.TypeName}}
return json.Marshal(&struct {
*Alias
CreatedAt *time.Time ` + "`json:\"created_at,omitempty\"`" + `
UpdatedAt *time.Time ` + "`json:\"updated_at,omitempty\"`" + `
}{
Alias: (*Alias)(&{{.Receiver}}),
})
}
`
第一种方法是最简洁且符合Go习惯的,它通过结构体嵌入和MarshalJSON重写实现了仅序列化时忽略特定字段的需求,同时保持了解序列化的正常功能。

