【Go】自定义json序列化

一、忽略字段

我们知道,通过tag,可以有条件地实现定制Go JSON序列化的方式,比如json:"abc,omitempty", 当字段的值为空的时候,我们可以在序列化后的数据中不包含这个值,而json:"-"可以直接不被JSON序列化,如果想被序列化key-,可以设置tag为json:"-,",加个逗号

二、改变一个字段显示

有下面这个结构体

type MyUser struct {
	ID       int64     `json:"id"`
	Name     string    `json:"name"`
	LastSeen time.Time `json:"lastSeen"`
}

如果临时想改变LastSeen字段显示为时间戳(或者密码我们不想打印到日志中,用***代替)

方案一

最简单的方式是引入另外一个辅助struct,在MarshalJSON中使用它进行正确的格式化:

func (u *MyUser) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		ID       int64  `json:"id"`
		Name     string `json:"name"`
		LastSeen int64  `json:"lastSeen"`
	}{
		ID:       u.ID,
		Name:     u.Name,
		LastSeen: u.LastSeen.Unix(),
	})
}

方案二

方案一在遇到多字段的时候会很麻烦,如果我们能把原始struct嵌入到新的struct中,并让它继承所有不需要改变的字段就太好了:

func (u *MyUser) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		LastSeen int64 `json:"lastSeen"`
		*MyUser
	}{
		LastSeen: u.LastSeen.Unix(),
		MyUser:   u,
	})
}

但是,上面这个运行是会有问题的—陷入死循环:辅助struct会继承原始struct的MarshalJSON

解决办法就是为原始类型起个别名,别名会有原始struct所有的字段,但是不会继承它的方法:

func (u *MyUser) MarshalJSON() ([]byte, error) {
	type Alias MyUser
	return json.Marshal(&struct {
		LastSeen int64 `json:"lastSeen"`
		*Alias
	}{
		LastSeen: u.LastSeen.Unix(),
		Alias:    (*Alias)(u),
	})
}

同样的技术也可以应用于UnmarshalJSON方法:

func (u *MyUser) UnmarshalJSON(data []byte) error {
	type Alias MyUser
	aux := &struct {
		LastSeen int64 `json:"lastSeen"`
		*Alias
	}{
		Alias: (*Alias)(u),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	u.LastSeen = time.Unix(aux.LastSeen, 0)
	return nil
}

三、使用 json.RawMessage

如果部分json文档没有标准格式,我们可以把原始的文本信息用string保存下来。

type TestObject struct {
	Field1 string
	Field2 json.RawMessage
}
var data TestObject
json.Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data)
should.Equal(` [1,2,3]`, string(data.Field2))

后续可以继续对Filed2调用Unmarshal

四、使用 json.Number

默认情况下,如果是 interface{} 对应数字的情况会是 float64 类型的。如果输入的数字比较大,这个表示会有损精度。所以可以 UseNumber() 启用 json.Number 来用字符串表示数字。

// 字符串中author字段不确定是string还是uint64时
type Record struct {
    AuthorRaw interface{} `json:"author"`
    Title     string      `json:"title"`
    URL       string      `json:"url"`

    AuthorEmail string
    AuthorID    uint64
}
func Decode(r io.Reader) (x *Record, err error) {
    x = new(Record)
    if err = json.NewDecoder(r).Decode(x); err != nil {
        return
    }
    switch t := x.AuthorRaw.(type) {
    case string:
        x.AuthorEmail = t
    case json.Number:
        var n uint64
        // We would shadow the outer `err` here by using `:=`
        n, err = t.Int64()
        x.AuthorID = n
    }
    return
}

当然,这里用json.RawMessage也是可以的。

五、一个json切分成两个struct

json.Unmarshal([]byte(`{
  "url": "attila@attilaolah.eu",
  "title": "Attila's Blog",
  "visitors": 6,
  "page_views": 14
}`), &struct {
  *BlogPost
  *Analytics
}{&post, &analytics})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot提供了自定义JSON序列化和反序列化的方式,可以通过实现`JsonSerializer`和`JsonDeserializer`接口来实现。 自定义JSON序列化: 1. 创建一个类,实现`JsonSerializer`接口,并重写`serialize`方法。在该方法中,可以自定义对象的序列化逻辑,将对象转换为JSON格式的字符串。 2. 在需要进行自定义序列化的字段上,使用`@JsonSerialize(using = YourSerializer.class)`注解,将自定义序列化类指定为该字段的序列化器。 自定义JSON序列化: 1. 创建一个类,实现`JsonDeserializer`接口,并重写`deserialize`方法。在该方法中,可以自定义JSON字符串的反序列化逻辑,将JSON字符串转换为对象。 2. 在需要进行自定义序列化的字段上,使用`@JsonDeserialize(using = YourDeserializer.class)`注解,将自定义的反序列化类指定为该字段的反序列化器。 示例代码如下: ```java // 自定义序列化器 public class CustomSerializer extends JsonSerializer<YourClass> { @Override public void serialize(YourClass value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 自定义序列化逻辑 gen.writeString(value.toString()); } } // 自定义序列化器 public class CustomDeserializer extends JsonDeserializer<YourClass> { @Override public YourClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 自定义序列化逻辑 String value = p.getValueAsString(); return new YourClass(value); } } // 在需要进行自定义序列化和反序列化的字段上使用注解 public class YourClass { @JsonSerialize(using = CustomSerializer.class) private String field1; @JsonDeserialize(using = CustomDeserializer.class) private String field2; // 省略其他代码 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值