为了账号安全,请及时绑定邮箱和手机立即绑定

什么输入会导致golang的json.Marshal返回错误?

什么输入会导致golang的json.Marshal返回错误?

Go
有只小跳蛙 2021-11-22 18:53:20
从文档:JSON 不能表示循环数据结构,Marshal 不处理它们。将循环结构传递给 Marshal 将导致无限递归。我遇到过这种情况,这会导致运行时恐慌。我想知道是否有人可以提供一个工作程序来演示 json.Marshal 返回非零错误的非恐慌情况。最好的答案显然包括使用的输入。
查看完整描述

4 回答

?
芜湖不芜

TA贡献1796条经验 获得超7个赞

只是为了补充乔纳森的回答, json.Marshal 函数可以返回两种类型的错误:UnsupportedTypeError或UnsupportedValueError


第一个可能是由 Jonathan 所说的,通过尝试对无效类型进行编组:


_, err := json.Marshal(make(chan int))

_, ok := err.(*json.UnsupportedTypeError) // ok == true

另一方面,您还可以通过传递无效值让 Marshal 函数返回错误:


_, err := json.Marshal(math.Inf(1))

_, ok := err.(*json.UnsupportedValueError) // ok == true


查看完整回答
反对 回复 2021-11-22
?
尚方宝剑之说

TA贡献1788条经验 获得超4个赞

更新:现在使用通道而不是 map[int]int 来引发错误


特定于 Go 的结构,例如func或chan拒绝序列化:


package main


import (

    "encoding/json"

    "fmt"

)


func main() {

    value := make(chan int)

    _, err := json.Marshal(value)

    fmt.Println(err)

}


查看完整回答
反对 回复 2021-11-22
?
慕码人8056858

TA贡献1803条经验 获得超6个赞

阅读源代码可以找到这样一个判断编码器的函数,如果不存在会返回编组错误:https : //github.com/golang/go/blob/master/src/encoding/json/encode.go


func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {

    // ignored

    switch t.Kind() {

    case reflect.Bool:

        return boolEncoder

    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

        return intEncoder

    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:

        return uintEncoder

    case reflect.Float32:

        return float32Encoder

    case reflect.Float64:

        return float64Encoder

    case reflect.String:

        return stringEncoder

    case reflect.Interface:

        return interfaceEncoder

    case reflect.Struct:

        return newStructEncoder(t)

    case reflect.Map:

        return newMapEncoder(t)

    case reflect.Slice:

        return newSliceEncoder(t)

    case reflect.Array:

        return newArrayEncoder(t)

    case reflect.Ptr:

        return newPtrEncoder(t)

    default:

        return unsupportedTypeEncoder

    }

}

我们可以在https://github.com/golang/go/blob/master/src/reflect/type.go找到各种枚举


所以不难看出,不在上述函数中的种类是无法编组的:


UnsafePointer,Complex64,Complex128,Chan,Func

例子:


        json.Marshal(unsafe.Pointer(nil)) // UnsafePointer

        json.Marshal(complex64(1))        // Complex64

        json.Marshal(complex128(1))       // Complex128

        json.Marshal(make(chan struct{})) // Chan

        json.Marshal(func() {})           // Func


查看完整回答
反对 回复 2021-11-22
?
临摹微笑

TA贡献1982条经验 获得超2个赞

前段时间我正在解决一个在golang中序列化/反序列化循环引用的问题,所有链接都指向这个问题。然而,这有点误导,因为问题更广泛。


如果你遇到了和我一样的情况,并且找不到如何处理循环引用的解决方案,你现在可以使用tahwil——我在 github 上发布的一个新库。据我所知,它现在是唯一一个以通用方式促进循环数据结构的序列化/反序列化的库。


自述文件提供了有关如何使用该库的信息,因此我将在此处仅复制示例。


编码:

package main


import (

    "encoding/json"

    "fmt"


    "github.com/go-extras/tahwil"

)


type Person struct {

    Name     string

    Parent   *Person

    Children []*Person

}


func main() {

    parent := &Person{

        Name: "Arthur",

        Children: []*Person{

            {

                Name: "Ford",

            },

            {

                Name: "Trillian",

            },

        },

    }

    parent.Children[0].Parent = parent

    parent.Children[1].Parent = parent

    v, err := tahwil.ToValue(parent)

    if err != nil {

        panic(err)

    }

    res, err := json.Marshal(v)

    if err != nil {

        panic(err)

    }

    fmt.Println(string(res))

}

解码:

package main


import (

    "encoding/json"

    "fmt"


    "github.com/go-extras/tahwil"

)


type Person struct {

    Name     string    `json:"name"`

    Parent   *Person   `json:"parent"`

    Children []*Person `json:"children"`

}


func prepareData() []byte {

    parent := &Person{

        Name: "Arthur",

        Children: []*Person{

            {

                Name: "Ford",

            },

            {

                Name: "Trillian",

            },

        },

    }

    parent.Children[0].Parent = parent

    parent.Children[1].Parent = parent

    v, err := tahwil.ToValue(parent)

    if err != nil {

        panic(err)

    }

    res, err := json.Marshal(v)

    if err != nil {

        panic(err)

    }

    return res

}


func main() {

    data := &tahwil.Value{}

    res := prepareData()

    err := json.Unmarshal(res, data)

    if err != nil {

        panic(err)

    }

    person := &Person{}

    err = tahwil.FromValue(data, person)

    if err != nil {

        panic(err)

    }

    fmt.Printf(`Name: %s

Children:

    - %s

    -- parent name: %s

    - %s

    -- parent name: %s

`, person.Name,

        person.Children[0].Name,

        person.Children[0].Parent.Name,

        person.Children[1].Name,

        person.Children[1].Parent.Name)

}

主要思想是将原始数据转换为tahwil.Value{},这实际上将refid'添加到您的所有字段中。每当tahwil遇到循环引用时,它都会用引用替换实际对象。之后,该图在技术上不再是循环的,因此可以编组为 json。


恢复数据意味着反向操作,即任何引用都将被指向对象的指针替换。


PS 为什么tahwil?我试图为这个名字找到一些不常见的词,并找到了一个阿拉伯语词(تحويل),意思是转换。


查看完整回答
反对 回复 2021-11-22
  • 4 回答
  • 0 关注
  • 180 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信