A common http response model using generics:
type HttpResp[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data T `json:"data"`
}
Sometimes the data does not return, it is null or string or number; in any case, I am not too concerned about its value.
If i use struct{}
will get error: json: cannot unmarshal string into Go struct field HttpResp[struct {}].data of type struct {}
resp := HttpResp[struct{}]{}
err := json.Unmarshal([]byte(`{"code":200,"msg":"ok","data": "ok"}`), &resp)
I currently found a clumsy way to solve it.
resp := HttpResp[json.RawMessage]{}
Is there a more elegant way?
if you doesn't know what type of is declared in json
(null or string or number), you can use *any
to mark Data
field as optional and then check resp.Data != nil
and field's type to cast:
func TestName(t *testing.T) {
resp := HttpResp[*any]{}
err := json.Unmarshal([]byte(`{"code":200,"msg":"ok","data": "ok"}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp)
fmt.Println(resp.Data != nil)
fmt.Println(*resp.Data)
fmt.Println((*resp.Data).(string))
fmt.Println(`----`)
resp = HttpResp[*any]{}
err = json.Unmarshal([]byte(`{"code":200,"msg":"ok","data": 1}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp)
fmt.Println(resp.Data != nil)
fmt.Println(*resp.Data)
fmt.Println((*resp.Data).(float64))
fmt.Println(`----`)
resp = HttpResp[*any]{}
err = json.Unmarshal([]byte(`{"code":200,"msg":"ok"}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp)
fmt.Println(resp.Data == nil)
}
{200 ok 0xc000112450}
true
ok
ok
----
{200 ok 0xc0001124a0}
true
1
1
----
{200 ok <nil>}
true
ā ļø But, I think , you does not need to use generic in this case.
Or try to use more "elegant" way to unmarshal struct
šš» create custom implementation of the Unmarshaller with types check after unmarshalling:
type StringFloatStruct struct {
Val any
}
func (b *StringFloatStruct) UnmarshalJSON(data []byte) error {
switch sdata := strings.TrimSpace(string(data)); {
case sdata == "null":
return nil
default: // for example
fdata, err := strconv.ParseFloat(sdata, 64)
if err == nil {
b.Val = fdata
return nil
}
b.Val = strings.Trim(sdata, "\"")
return nil
}
}
func (b *StringFloatStruct) IsNil() bool {
return b.Val == nil
}
func (b *StringFloatStruct) ValString() (string, bool) {
v, ok := b.Val.(string)
return v, ok
}
func (b *StringFloatStruct) ValFloat64() (float64, bool) {
v, ok := b.Val.(float64)
return v, ok
}
func TestV2(t *testing.T) {
resp := HttpResp[StringFloatStruct]{}
err := json.Unmarshal([]byte(`{"code":200,"msg":"ok","data": "ok"}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp)
fmt.Println(resp.Data.IsNil())
fmt.Println(resp.Data.ValString())
fmt.Println(resp.Data.ValFloat64())
fmt.Println(`----`)
resp = HttpResp[StringFloatStruct]{}
err = json.Unmarshal([]byte(`{"code":200,"msg":"ok","data": 1}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp.Data.IsNil())
fmt.Println(resp.Data.ValString())
fmt.Println(resp.Data.ValFloat64())
fmt.Println(`----`)
resp = HttpResp[StringFloatStruct]{}
err = json.Unmarshal([]byte(`{"code":200,"msg":"ok"}`), &resp)
if err != nil {
t.Fatal(err)
}
fmt.Println(resp.Data.IsNil())
fmt.Println(resp.Data.ValString())
fmt.Println(resp.Data.ValFloat64())
}
false
ok true
0 false
----
false
false
1 true
----
true
false
0 false