我正在处理第三方基于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
}
Run Code Online (Sandbox Code Playgroud)
有没有解决的办法?
正确的解决方案是"克隆"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
}
Run Code Online (Sandbox Code Playgroud)
然后你就可以做这样的事情:
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
}
Run Code Online (Sandbox Code Playgroud)
随意调整UnmarshalJSON您的需求,我的间距非常严格.归功于Dave C,这很大程度上受到了他对另一个问题的评论的启发(其中还包含了上述解决方案的更多变体).
或者,您可以使用正则表达式预处理JSON数据,但如果上面的解决方案是可行的选项,则不要这样做,它会更快.
re := regexp.MustCompile(`(":\s*)([\d\.]+)(\s*[,}])`)
rawJsonByteArray = re.ReplaceAll(rawJsonByteArray, []byte(`$1"$2"$3`))
Run Code Online (Sandbox Code Playgroud)