来自文档:JSON不能表示循环数据结构,Marshal也不处理它们。将循环结构传递给Marshal将导致无限递归。我经历过这种情况,它会导致运行时死机。我想知道的是,是否有人能提供一个工作程序来演示json.marshal返回非空错误时的非恐慌情况,最好的答案是清楚地包括所使用的输入。
nhaq1z211#
为了补充Jonathan的回答,json.marshal函数可以返回两种类型的错误:UnsupportedTypeError或UnsupportedValueError第一个错误可能是由于尝试封送一个无效类型而引起的,正如Jonathan所说:
UnsupportedTypeError
UnsupportedValueError
_, 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
xiozqbni2#
更新:现在使用通道而不是map[int]int来引出错误
Go语言特有的结构,例如func或chan拒绝序列化:
func
chan
package main import ( "encoding/json" "fmt" ) func main() { value := make(chan int) _, err := json.Marshal(value) fmt.Println(err) }
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
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)) }
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),意思是 * 转换 *。
tahwil.Value{}
refid
tahwil
5条答案
按热度按时间nhaq1z211#
为了补充Jonathan的回答,json.marshal函数可以返回两种类型的错误:
UnsupportedTypeError
或UnsupportedValueError
第一个错误可能是由于尝试封送一个无效类型而引起的,正如Jonathan所说:
另一方面,您也可以通过传递无效值来使Marshal函数返回错误:
xiozqbni2#
更新:现在使用通道而不是map[int]int来引出错误
Go语言特有的结构,例如
func
或chan
拒绝序列化:nzrxty8p3#
阅读源代码可以发现这样一个函数来判断一个编码器如果不存在会返回marshal错误:https://github.com/golang/go/blob/master/src/encoding/json/encode.go
我们可以在https://github.com/golang/go/blob/master/src/reflect/type.go中找到各种枚举
因此不难看出,不在上述函数中的类无法编组:
示例:
jaxagkaj4#
https://go.dev/play/p/_Z29viT82CR .
看看这个-
1tu0hz3e5#
不久前,我在golang中解决了一个循环引用的序列化/反序列化问题,所有的链接都指向这个问题,然而,由于问题的范围更广,所以有点误导。
如果你遇到了和我一样的情况,并且找不到处理循环引用的解决方案,你现在可以使用tahwil--我在github上发布的一个新库,据我所知,它是目前唯一一个以通用方式促进循环数据结构的序列化/反序列化的库。
Readme给出了如何使用库的信息,所以我在这里只重复示例。
编码:
解码:
其主要思想是将原始数据转换为
tahwil.Value{}
,这实际上是将refid
添加到所有字段中,每当tahwil
遇到循环引用时,它就用引用替换实际对象,此后图形在技术上不再是循环的,因此可以编组到json。恢复数据意味着反向操作,即任何引用都将由指向对象的指针替换。
为什么是
tahwil
?我试着为这个名字找一些不常见的词,结果找到了一个阿拉伯语单词(şşşaultible),意思是 * 转换 *。