猿问

解码器无法解组时如何获取 JSON?

我正在使用 ajson.Decoder来解码通过网络流传递的 JSON。它工作正常,但是每当有人发送不符合架构的数据时(例如,当结构的字段类型为无符号时发送负整数),它会返回一个错误,并带有一条无法确定违规属性的模糊消息。这使得调试更加困难。


有没有办法提取解码器在出错时试图解组的 JSON?这是一个可重现的小片段:


package main


import (

    "bytes"

    "fmt"

    "encoding/json"

)


func main() {

    buff := bytes.NewBufferString("{\"bar\": -123}")

    decoder := json.NewDecoder(buff)


    var foo struct{

        Bar uint32

    }

    if err := decoder.Decode(&foo); err != nil {

        fmt.Println(err)

        fmt.Println("TODO: how to get JSON that caused this error?")

    } else {

        fmt.Println(foo.Bar)

    }

}

或在操场上:https : //play.golang.org/p/-WdYBkYEzJ


森林海
浏览 234回答 3
3回答

波斯汪

错误中有一些信息,它是类型 *json.UnamrshalTypeErrortype 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}您可以获取 json 字符串中的偏移量、reflect.Type被解组到的字段的偏移量以及 Value 的 json 描述。这仍然会给实现自己的解组逻辑的类型带来问题,问题 11858引用了该逻辑

MMTTMM

从 Go 1.8 开始,这现在是可能的。该UnmarshalTypeError类型现在包含Struct和Field值,这些值提供了导致类型不匹配的结构和字段的名称。package mainimport (    "bytes"    "fmt"    "encoding/json")func main() {    buff := bytes.NewBufferString("{\"bar\": -123}")    decoder := json.NewDecoder(buff)    var foo struct{        Bar uint32    }    if err := decoder.Decode(&foo); err != nil {        if terr, ok := err.(*json.UnmarshalTypeError); ok {                fmt.Printf("Failed to unmarshal field %s \n", terr.Field)        } else {            fmt.Println(err)        }    } else {        fmt.Println(foo.Bar)    }}错误消息字符串也已更改为包含此新信息。去 1.8:json: cannot unmarshal number -123 into Go struct field .Bar of type uint32转到 1.7 及更早版本:json: cannot unmarshal number -123 into Go value of type uint32

哔哔one

您可以decode.Token()在Playground和以下(根据您的示例修改)中使用这样的方法获取每个元素、键、值甚至分隔符:package mainimport (    "bytes"    "encoding/json"    "fmt")func main() {    buff := bytes.NewBufferString(`{"foo": 123, "bar": -123, "baz": "123"}`)    decoder := json.NewDecoder(buff)    for {        t, err := decoder.Token()        if _, ok := t.(json.Delim); ok {            continue        }        fmt.Printf("type:%11T | value:%5v //", t, t)        switch t.(type) {        case uint32:            fmt.Println("you don't see any uints")        case int:            fmt.Println("you don't see any ints")        case string:            fmt.Println("handle strings as you will")        case float64:            fmt.Println("handle numbers as you will")        }        if !decoder.More() {            break        }        if err != nil {            fmt.Println(err)        }    }}这将输出type:     string | value:  foo //handle strings as you willtype:    float64 | value:  123 //handle numbers as you willtype:     string | value:  bar //handle strings as you willtype:    float64 | value: -123 //handle numbers as you willtype:     string | value:  baz //handle strings as you willtype:     string | value:  123 //handle strings as you will您可以根据需要打开类型并处理每个类型。我也展示了一个简单的例子,结果中的每个“//comments”都是基于类型的条件。您还会注意到每个数字的类型都是 float64,尽管它们适合 int,或者在“foo”值的情况下甚至是 uint,我在 switch 中检查了这些类型,但它们从未被使用过。您必须提供自己的逻辑,以便将 float64 值转换为您想要的类型(如果可能)并处理不会转换为特殊情况、错误或任何您想要的类型。
随时随地看视频慕课网APP

相关分类

Go
我要回答