Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-json-custom-field

前言

实现一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

type QueryReq struct {
Status Nullable[int32] `json:"status,omitempty"`
}

if req.Status.Val != nil {
filters = append(filters, func(d *gorm.DB) *gorm.DB {
return d.Where("status=?", *req.Status.Val)
})
}

package entities

import (
"encoding/json"
"fmt"
)

// Nullable 是一个通用类型,可以兼容 int/string/null/"" 的 JSON 输入
type Nullable[T any] struct {
Val *T
}

// UnmarshalJSON 支持 null, "", 数字, 字符串数字
func (n *Nullable[T]) UnmarshalJSON(data []byte) error {
// null
if string(data) == "null" {
n.Val = nil
return nil
}

// 空字符串 → nil
if string(data) == `""` {
n.Val = nil
return nil
}

var s string
if err := json.Unmarshal(data, &s); err == nil {
if s == "" {
n.Val = nil
return nil
}
// 转换为数字的情况
var anyVal any
if err := json.Unmarshal([]byte(s), &anyVal); err == nil {
switch v := anyVal.(type) {
case float64:
// 处理 int32/int64 类型
switch any(*new(T)).(type) {
case int32:
val := int32(v)
n.Val = any(&val).(*T)
return nil
case int64:
val := int64(v)
n.Val = any(&val).(*T)
return nil
case string:
val := s
n.Val = any(&val).(*T)
return nil
}
}
}
// 普通字符串情况
if _, ok := any(*new(T)).(string); ok {
val := s
n.Val = any(&val).(*T)
return nil
}
}

// 直接解析目标类型
var t T
if err := json.Unmarshal(data, &t); err == nil {
n.Val = &t
return nil
}

return fmt.Errorf("invalid value for Nullable: %s", string(data))
}

// MarshalJSON 保证输出 null 或值
func (n Nullable[T]) MarshalJSON() ([]byte, error) {
if n.Val == nil {
return []byte("null"), nil
}
return json.Marshal(*n.Val)
}