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

时间:2016-05-09 10:37:03

标签: json go

我使用json.Decoder解码通过网络流传送的JSON。它工作正常,但每当有人发送的数据不符合架构时(例如,当struct的字段类型为unsigned时发送一个负整数),它会返回一个错误,其中含有一个模糊的消息,指出了违规的属性。这使得调试更加困难。

有没有办法提取解码器在出错时试图解组的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

3 个答案:

答案 0 :(得分:2)

错误中有一些信息属于*json.UnamrshalTypeError

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
}

您可以获取json字符串中的偏移量,正在解组的字段的reflect.Type以及值的json描述。这仍然会对实现自己的解组逻辑的类型造成问题,该逻辑由issue 11858引用

答案 1 :(得分:0)

您可以使用decode.Token()中的package main import ( "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 will
type:    float64 | value:  123 //handle numbers as you will
type:     string | value:  bar //handle strings as you will
type:    float64 | value: -123 //handle numbers as you will
type:     string | value:  baz //handle strings as you will
type:     string | value:  123 //handle strings as you will

这将输出

{{1}}

您可以根据需要打开类型并处理每个类型。我已经展示了一个简单的例子,结果中的每个“//注释”都是基于类型的条件。

你还会注意到每个数字的类型都是float64,虽然它们适合于int,甚至在“foo”值的情况下也是uint,我在交换机中检查这些类型但它们永远不会得到用过的。您必须提供自己的逻辑,以便将float64值转换为您想要的类型,并处理不会转换为特殊情况或错误或任何您想要的类型的类型。

答案 2 :(得分:0)

从Go 1.8开始,现在可以实现。 UnmarshalTypeError类型现在包含StructField值,这些值提供导致类型不匹配的结构和字段的名称。

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 {
        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

Go 1.7及更早版本:

json: cannot unmarshal number -123 into Go value of type uint32