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

dy2hfwbg  于 2023-03-13  发布在  Go
关注(0)|答案(5)|浏览(269)

来自文档:
JSON不能表示循环数据结构,Marshal也不处理它们。将循环结构传递给Marshal将导致无限递归。
我经历过这种情况,它会导致运行时死机。
我想知道的是,是否有人能提供一个工作程序来演示json.marshal返回非空错误时的非恐慌情况,最好的答案是清楚地包括所使用的输入

nhaq1z21

nhaq1z211#

为了补充Jonathan的回答,json.marshal函数可以返回两种类型的错误:UnsupportedTypeErrorUnsupportedValueError
第一个错误可能是由于尝试封送一个无效类型而引起的,正如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
xiozqbni

xiozqbni2#

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

Go语言特有的结构,例如funcchan拒绝序列化:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    value := make(chan int)
    _, err := json.Marshal(value)
    fmt.Println(err)
}
nzrxty8p

nzrxty8p3#

阅读源代码可以发现这样一个函数来判断一个编码器如果不存在会返回marshal错误: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
jaxagkaj

jaxagkaj4#

https://go.dev/play/p/_Z29viT82CR .
看看这个-

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Id      int64  `json:"id"`
    Name    string `json:"name"`
    Persons []Person
}

func main() {
    f := Person{Id: 1, Name: "shriprasad"}
    f.Persons = append(f.Persons, f)

    result, err := json.Marshal(f)
    fmt.Println("error " + err.Error())
    fmt.Println(string(result))

}
1tu0hz3e

1tu0hz3e5#

不久前,我在golang中解决了一个循环引用的序列化/反序列化问题,所有的链接都指向这个问题,然而,由于问题的范围更广,所以有点误导。
如果你遇到了和我一样的情况,并且找不到处理循环引用的解决方案,你现在可以使用tahwil--我在github上发布的一个新库,据我所知,它是目前唯一一个以通用方式促进循环数据结构的序列化/反序列化的库。
Readme给出了如何使用库的信息,所以我在这里只重复示例。

编码:

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。
恢复数据意味着反向操作,即任何引用都将由指向对象的指针替换。
为什么是tahwil?我试着为这个名字找一些不常见的词,结果找到了一个阿拉伯语单词(şşşaultible),意思是 * 转换 *。

相关问题