Skip to content

Instantly share code, notes, and snippets.

@WonderfulSoap
Last active September 21, 2023 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WonderfulSoap/18a14da135f659d5350f36bdbe439b6a to your computer and use it in GitHub Desktop.
Save WonderfulSoap/18a14da135f659d5350f36bdbe439b6a to your computer and use it in GitHub Desktop.
package main
// 某个实际项目复杂业务的DTO定义,字段数量规模相似,(仅做演示,并非所有字段都为int类型)
// 大部分字段都不可为null,只有少数几个字的可null
type Data2 struct {
A *int
B *int
C *int
D *int
E *int
F *int
G *int
H *int
I *int //可null字段
J *int //可null字段
K *int
L *int
M *int
N *int
O *int // 可null字段
P *int
Q *int // 可null字段
R *int
S *int
T *int
U *int
V *int
W *int
X *int
Y *int
Z *int
Obj1 *struct {
A *int
B *int
C *int
D *int
E *int
F *int
G *int
H *int
I *int //可null字段
J *int //可null字段
K *int
L *int
M *int
N *int
O *int // 可null字段
P *int
Q *int // 可null字段
R *int
S *int
T *int
}
}
// validate 判断所有不可为null的字段是否为null。为null返回错误。标记为可null字段的无需判断
func (d Data2) Validate() error {
if d.A == nil || d.B == nil || d.C == nil || d.D == nil || d.E == nil || d.F == nil || d.G == nil || d.H == nil|| ...... {
return fmt.Errorf("validation error")
}
return nil
}
package main
func Calculate(name string, data Data2) int {
// 这里已经有两个bug了, data.Obj1.I 和 data.I 为可null字段,使用前必须判断nil
return (*data.A) * (*data.B) * (*data.C) * (*data.D) * (*data.I) * (*data.Obj1.A) * (*data.Obj1.B) * (*data.Obj1.I)
}
@QuantumGhost
Copy link

QuantumGhost commented Sep 21, 2023

This is what you want:

type jsonScalar interface {
	int | int64 | float32 | float64 | string
}

type Strict[T jsonScalar] struct {
	value T
}

func NewStrict[T jsonScalar](value T) Strict[T] {
	return Strict[T]{value: value}
}

func (s *Strict[T]) UnmarshalJSON(data []byte) error {
	if data[0] == 'n' {
		return errors.New("got null")
	}
	var dest T
	err := json.Unmarshal(data, &dest)
	if err != nil {
		return err
	}
	s.value = dest
	return nil
}

func (s *Strict[T]) MarshalJSON() ([]byte, error) {
	value := s.value
	return json.Marshal(value)
}

func TestUnmarshal(t *testing.T) {
	type P struct {
		Price int `json:"price"`
	}

	type StrictP struct {
		Price Strict[int] `json:"price"`
	}

	var p P
	err := json.Unmarshal([]byte(`{"price" : null}`), &p)
	fmt.Printf("error: %s\n", err)
	fmt.Printf("p: %#v\n", p)

	var sp StrictP
	err = json.Unmarshal([]byte(`{"price" : null }`), &sp)
	fmt.Printf("error: %s\n", err)
	fmt.Printf("p: %#v\n", sp)
	sp = StrictP{}
	err = json.Unmarshal([]byte(`{"price" : 1 }`), &sp)
	fmt.Printf("error: %s\n", err)
	fmt.Printf("p: %#v\n", sp)
}

Error handling and wrapping are omitted for simplicity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment