我有以下示例代码:
type Num struct {
X uint64 `json:"x,string"`
Y float64 `json:"y,string"`
}
现在,如果我运行代码
js := []byte(`{"x": "123", "y": "1.23"}`)
var n Num
err := json.Unmarshal(js, &n)
它可以解析。
但是如果我将JSON更改为
js := []byte(`{"x": 123, "y": 1.23}`)
它返回一个错误。
我能理解的结果。
现在,我的问题是如何使它同时接受字符串和uint64 / float64?
答案 0 :(得分:1)
您将需要定义一个实现json.Unmarshaler
interface的自定义类型,以使该值可以是字符串或纯数字。
以下示例显示了如何针对单一类型(uint64
)进行操作;您将需要为其他任何数字类型(Go Playground)重复该模式:
type Uint64 uint64
type Num struct {
X Uint64 `json:"x"`
}
func (u *Uint64) UnmarshalJSON(bs []byte) error {
str := string(bs) // Parse plain numbers directly.
if bs[0] == '"' && bs[len(bs)-1] == '"' {
// Unwrap the quotes from string numbers.
str = string(bs[1 : len(bs)-1])
}
x, err := strconv.ParseUint(str, 10, 64)
if err != nil {
return err
}
*u = Uint64(x)
return nil
}
func main() {
ss := []string{`{"x":"123"}`, `{"x":123}`}
var n Num
for _, s := range ss {
err := json.Unmarshal([]byte(s), &n)
fmt.Printf("OK: s=%-11s n=%#v err=%v\n", s, n, err)
}
// OK: s={"x":"123"} n=main.Num{X:0x7b} err=<nil>
// OK: s={"x":123} n=main.Num{X:0x7b} err=<nil>
}
答案 1 :(得分:1)
在@maerics答案的基础上,您可以将这两种情况都推迟到通常的json unmarshaler上,这感觉更强大:
package main
import (
"encoding/json"
"errors"
"fmt"
)
type Uint64 uint64
type Num struct {
X Uint64 `json:"x"`
}
func (u *Uint64) UnmarshalJSON(bs []byte) error {
var i uint64
if err := json.Unmarshal(bs, &i); err == nil {
*u = Uint64(i)
return nil
}
var s string
if err := json.Unmarshal(bs, &s); err != nil {
return errors.New("expected a string or an integer")
}
if err := json.Unmarshal([]byte(s), &i); err != nil {
return err
}
*u = Uint64(i)
return nil
}
func main() {
ss := []string{`{"x":"123"}`, `{"x":123}`, `{"x":0.12}`}
var n Num
for _, s := range ss {
err := json.Unmarshal([]byte(s), &n)
fmt.Printf("OK: s=%-11s n=%#v err=%v\n", s, n, err)
}
}
给出
OK: s={"x":"123"} n=main.Num{X:0x7b} err=<nil>
OK: s={"x":123} n=main.Num{X:0x7b} err=<nil>
OK: s={"x":0.12} n=main.Num{X:0x7b} err=expected a string or an integer