Go 自定义序列化

实现MarshalJSON() ([]byte, error) 方法 ,序列化后可以把原来的枚举数转化为枚举数对应的字符串

实现UnmarshalJSON([]byte) error方法,可以把byte中的枚举的字符串转化为对应枚举字符串的枚举数

实现String() string方法,方便按照我们想看的方式打印出来fmt.Println(),类似与python的__str____repr__

package main_test

import (
	"errors"
	"fmt"
	"strings"
	"testing"

	"github.com/bytedance/sonic"
)

type A struct {
	B B
	C string
}

type B int8

const (
	UP B = iota
	DOWN
	PENDING
)

var (
	StatusValueMap = map[B]string{
		UP:      "up",
		DOWN:    "down",
		PENDING: "pending",
	}
)

//实现 MarshalJSON  对枚举值进行转化
func (b B) MarshalJSON() ([]byte, error) {
	if v, ok := StatusValueMap[b]; ok {
		return []byte(`"` + v + `"`), nil
	}
	return nil, errors.New("枚举未定义")
}

//实现 UnmarshalJSON  对枚举值进行转化
func (b *B) UnmarshalJSON(data []byte) error {
	s := strings.Trim(string(data), `"`)
	for k, v := range StatusValueMap {
		if v == s {
			b = &k
			return nil
		}
	}
	return errors.New("未转化成功")
}

//实现String方法,方便打印查看
func (b B) String() string {
	return StatusValueMap[b]
}

func TestAbc(t *testing.T) {
	a := A{
		B: UP,
		C: "111",
	}
    //json标准库和sonic第三方库都可以
	// s, err := json.Marshal(a)
	s, err := sonic.Marshal(a)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(s)) //{"B":"up","C":"111"}  可以看到,已经将数字序列化成数字对应的字符串
	b := &A{}
	// err = json.Unmarshal(s, b)
	err = sonic.Unmarshal(s, b)
	if err != nil {
		panic(err)
	}
    fmt.Printf("%+v\n", b) //&{B:up C:111}  //已经将字符串反序列化为数字,这里显示up是因为实现了String()方法,可以看到,下面是等于0的
	fmt.Println(b.B == 0)  //true
}

遇到的坑

直接应用项为枚举时

如果结构体是如下这种

type A struct {
	Title   string `json:"title" gorm:"column:title;uniqueIndex:idx_title" validate:"required"`
	Author  string `json:"author" gorm:"column:author;not null" validate:"required"`
	Summary string `json:"summary" gorm:"column:summary;not null"`
	Content string `json:"content" gorm:"column:content"`
    //这里省略了后面跟的Status,是可以正常使用的,但是如果实现了序列化的三个方法,会导致整个结构体只有status被序列化,其他的被忽略
	Status  `json:"status" gorm:"column:status;not null"`
}
// Status Status枚举
type Status int8

const (
	DRAFT Status = 1 + iota
	PUBLISHED
)



// 实现序列化的方法

//新建对应status序列化对应的字符串
var (
	StatusValueMap = map[Status]string{
		DRAFT:     "DRAFT",
		PUBLISHED: "PUBLISHED",
	}
)


// 实现方法 MarshalJSON()、UnmarshalJSON()、String()
// 实现 MarshalJSON  对枚举值进行转化
func (sta Status) MarshalJSON() ([]byte, error) {
	if v, ok := StatusValueMap[sta]; ok {
		return []byte(`"` + v + `"`), nil
	}
	return nil, errors.New("枚举未定义")
}

// 实现 UnmarshalJSON  对枚举值进行转化
func (sta *Status) UnmarshalJSON(data []byte) error {
	s := strings.Trim(string(data), `"`)
	for k, v := range StatusValueMap {
		if v == s {
			sta = &k
			return nil
		}
	}
	return errors.New("未转化成功")
}

// 实现String方法,方便打印查看
func (sta Status) String() string {
	return StatusValueMap[sta]
}

在A结构体中,继承了枚举Status的结构体

这个时候,如果Status实现了序列化的三个方法MarshalJSON()UnmarshalJSON()String()

则会导致结构体A序列化时,只显示Status序列化的值,而A中的其他项都不会被序列化


序列化

func main() {
	a := &A{}
	res, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(res))
}
/*
结果为
"DRAFT"
*/

显然这不是我们想要的


正确的继承方法是

type A struct {
	Title   string `json:"title" gorm:"column:title;uniqueIndex:idx_title" validate:"required"`
	Author  string `json:"author" gorm:"column:author;not null" validate:"required"`
	Summary string `json:"summary" gorm:"column:summary;not null"`
	Content string `json:"content" gorm:"column:content"`
    //这里需要写清楚,不要省略第二个Status
	Status  Status `json:"status" gorm:"column:status;not null"`
}
// Status Status枚举
type Status int8

const (
	DRAFT Status = 1 + iota
	PUBLISHED
)

结果

{"title":"","author":"","summary":"","content":"","status":"DRAFT"}

不要省略Status后跟的Status

posted @   厚礼蝎  阅读(207)  评论(0编辑  收藏  举报
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示