我正在处理第三方基于 JSON 的 API。通常它会将所有数字用引号引起来,但有时不会。我对此无能为力。
我正在尝试提出一个解决方案,无论数字是否被引用,它都会解析数字。标准库提供了一个 ,string
字段标签,它允许将数字字段映射到带引号的值,但不幸的是,如果它不在引号中,它就无法处理该值。
func test(s string) {
err := json.Unmarshal([]byte(s), &struct {
F1 float64
F2 float64 `json:",string"`
}{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("success")
}
func main() {
test(`{"f1": 1.23}`) // success
test(`{"f1": "1.23"}`) // cannot unmarshal string into Go value of type float64
test(`{"f2": 1.23}`) // invalid use of ,string struct tag, trying to unmarshal unquoted value into ...
test(`{"f2": "1.23"}`) // success
}
有解决办法吗?
最佳答案
正确的解决方案是“克隆”float64 并为其定义自定义 MarshalJSON
和 UnmarshalJSON
:
type jsonFloat64 float64
func (f jsonFloat64) MarshalJSON() ([]byte, error) {
return json.Marshal(float64(f))
}
func (f *jsonFloat64) UnmarshalJSON(data []byte) error {
if len(data) >= 2 && data[0] == '"' && data[len(data)-1] == '"' {
data = data[1 : len(data)-1]
}
var tmp float64
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}
*f = jsonFloat64(tmp)
return nil
}
然后你就可以做这样的事情了:
func test(s string) {
err := json.Unmarshal([]byte(s), &struct {
F jsonFloat64
}{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("success")
}
func main() {
test(`{"f": 1.23}`) // success
test(`{"f": "1.23"}`) // success
}
请根据您的需要随意调整 UnmarshalJSON
,我的间距非常严格。归功于Dave C , 这在很大程度上受到了 his comment on another question 的启发(在上面的解决方案中也有 more variations)。
或者,您可以使用正则表达式预处理 JSON 数据,但如果上述解决方案可行,请不要这样做,这样会快得多。
re := regexp.MustCompile(`(":\s*)([\d\.]+)(\s*[,}])`)
rawJsonByteArray = re.ReplaceAll(rawJsonByteArray, []byte(`$1"$2"$3`))
关于json - 如何在 Go 中解析普通和引用的 JSON 数字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31625511/