如何在enconding / json的UnmarshalTypeError中使用偏移值以获得更好的错误处理?

时间:2016-11-22 17:41:15

标签: json go

一年多前,Go为Offset类型添加了json.UnmarshalTypeError值(有关上下文,请参阅已结束的问题here)。偏移值背后的目的是有意义的,但我不确定在读取一个类型为io.ReadCloser的go http响应体时如何使用它。

// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError struct {
    Value  string       // description of JSON value - "bool", "array",   "number -5"
    Type   reflect.Type // type of Go value it could not be assigned to
    Offset int64        // error occurred after reading Offset bytes
}

例如:

var body CustomType
decoderErr := json.NewDecoder(response.Body).Decode(&body)

if decoderErr != nil {

    if typeError, ok := decoderErr.(*json.UnmarshalTypeError); ok {
        // Do something with typeError.Offset here
    }


}

在发现错误时,我已经通过response.Bodyjson.NewDecoder...读取。我正在寻找一种方法来再次读取response.Body,但只能通过使用typeError中的Offset值来达到错误点。

1 个答案:

答案 0 :(得分:2)

由于你想重用请求体,你应该在解组体之前读取并存储体,然后如果存在JSON语法或类型错误,你可以使用之前存储的体返回更有用的错误。

概念证明:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

type Hello struct {
    Name    string `json:"name"`
    Message string `json:"message"`
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        b, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "Error reading body", 400)
            return
        }

        h := &Hello{}
        if err := json.Unmarshal(b, &h); err != nil {
            var msg string
            switch t := err.(type) {
            case *json.SyntaxError:
                jsn := string(b[0:t.Offset])
                jsn += "<--(Invalid Character)"
                msg = fmt.Sprintf("Invalid character at offset %v\n %s", t.Offset, jsn)
            case *json.UnmarshalTypeError:
                jsn := string(b[0:t.Offset])
                jsn += "<--(Invalid Type)"
                msg = fmt.Sprintf("Invalid value at offset %v\n %s", t.Offset, jsn)
            default:
                msg = err.Error()
            }
            http.Error(w, msg, 400)
            return
        }

        w.Write([]byte(`Good to go!`))
    })

    if err := http.ListenAndServe(":8000", nil); err != nil {
        log.Fatal(err)
    }
}