我正在获取并解码一个包含错误的大型 JSON 响应。现在我需要找出错误在哪里!我读到了有关 json.SyntaxError 的内容,但我正在努力找出如何使用它。
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"text/template"
"time"
)
type Movie struct {
Title string `json:"title"`
PublishedAt time.Time `json:"published_at"`
}
func main() {
req, _ := http.NewRequest("GET", "https://s.natalian.org/2016-12-07/debugme2.json", nil)
resp, err := http.DefaultClient.Do(req)
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
_, err = dec.Token()
for dec.More() {
var m Movie
if err = dec.Decode(&m); err != nil {
fmt.Println(err)
fmt.Println("Bad", m)
// https://blog.golang.org/error-handling-and-go
if serr, ok := err.(*json.SyntaxError); ok {
fmt.Println("Syntax error", serr)
}
} else {
fmt.Println("Good", m)
}
tmpl := template.Must(template.New("test").Parse("OUTPUT: {{ if .Title }}{{.Title}}{{ if .PublishedAt }} was published at {{.PublishedAt}} {{ end }}{{end}}\n"))
tmpl.Execute(os.Stdout, m)
}
}
Run Code Online (Sandbox Code Playgroud)
我缺少什么?任何工具、策略或建议将不胜感激。我的输出目前看起来像:
Good {foobar 2016-11-24 16:17:12 +0800 SGT}
OUTPUT: foobar was published at 2016-11-24 16:17:12 +0800 SGT
parsing time ""null"" as ""2006-01-02T15:04:05Z07:00"": cannot parse "null"" as "2006"
Bad {barbar 0001-01-01 00:00:00 +0000 UTC}
OUTPUT: barbar was published at 0001-01-01 00:00:00 +0000 UTC
Good { 1999-12-24 16:11:12 +0200 +0200}
OUTPUT:
Good {Something else entirely 2000-01-24 16:11:12 +0200 +0200}
OUTPUT: Something else entirely was published at 2000-01-24 16:11:12 +0200 +0200
Run Code Online (Sandbox Code Playgroud)
但我的 stderr 中需要类似的东西来更好地调试问题:
Line 8: published_at is invalid
Run Code Online (Sandbox Code Playgroud)
也许还有标题的一些上下文,这样我就可以告诉 API 后端团队他们的 JSON 响应中有错误。
额外问题:此外,我不想打印值0001-01-01 00:00:00 +0000 UTC,因为它实际上是空的。我其实并不介意它丢失。
我找到了一些解决方案:
if err := json.Unmarshal([]byte(data), &myStruct); err != nil {
if jsonErr, ok := err.(*json.SyntaxError); ok {
problemPart := data[jsonErr.Offset-10 : jsonErr.Offset+10]
err = fmt.Errorf("%w ~ error near '%s' (offset %d)", err, problemPart, jsonErr.Offset)
}
}
Run Code Online (Sandbox Code Playgroud)
它会打印类似的东西
invalid character 'n' after object key:value pair ~ error near 'rence\","numberOfBil' (offset 14557)
Run Code Online (Sandbox Code Playgroud)
既接受空值又不打印任何内容(如果published_at为空)的一种方法是将PublishedAt字段设置为指针值:
type Movie struct {\n Title string `json:"title"`\n PublishedAt *time.Time `json:"published_at"`\n}\nRun Code Online (Sandbox Code Playgroud)\n\n输入字符串是有效的JSON,因此 json 包不会引发SyntaxError.
该json包还有一些其他错误类型,例如UnmarshalTypeError,当 json 与 nuilt-in 类型不匹配(例如 : string, int, array...)时发生错误时会引发该错误类型。
不幸的是,当它调用自定义UnmarshalJSON()函数时,包看起来json会返回原始错误:
package main\n\nimport (\n "bytes"\n "encoding/json"\n "fmt"\n "time"\n)\n\n// check the full type of an error raised when Unmarshaling a json string\nfunc main() {\n var test struct {\n Clock time.Time\n }\n buf := bytes.NewBufferString(`{"Clock":null}`)\n dec := json.NewDecoder(buf)\n\n // ask to decode an invalid null value into a flat time.Time field :\n err := dec.Decode(&test)\n\n // print the details of the returned error :\n fmt.Printf("%#v\\n", err)\n}\n\n// Output :\n&time.ParseError{Layout:"\\"2006-01-02T15:04:05Z07:00\\"", Value:"null", LayoutElem:"\\"", ValueElem:"null", Message:""}\nRun Code Online (Sandbox Code Playgroud)\n\nhttps://play.golang.org/p/fhZxVpOflb
\n\n最终的错误直接来自time包,而不是某种UnmarshalError来自json,它至少可以告诉您“尝试在这个偏移量解组值时发生此错误”,并且该错误本身不会为您提供上下文。
你可以专门找类型*time.ParseError错误中的类型:
if terr, ok := err.(*time.ParseError); ok {\n // in the example : Movie has one single time.Time field ;\n // if a time.ParseError occured, it was while trying to read that field\n fmt.Println("Error when trying to read \'published_at\' value", terr)\n\n // you can leave the field to its zero value,\n // or if you switched to a pointer field :\n m.PublishedAt = nil\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果您碰巧有多个时间字段(例如:ProducedAt\xc2\xa0andPublishedAt),您仍然可以查看哪个字段的值为零:
if terr, ok := err.(*time.ParseError); ok {\n if m.ProducedAt.IsZero() {\n fmt.Println("Error when trying to read \'produced_at\' value", terr)\n }\n\n if m.PublishedAt == zero {\n fmt.Println("Error when trying to read \'published_at\' value", terr)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n顺便说一下:正如文档中所指定的,“0001-01-01 00:00:00 UTC”是 go 团队为 go 选择的零值time.Time零值选择的零值。