我想在结构化为json时将一个RLock / RUnlock添加到结构中。
下面的例子显示了我尝试做的事情。但是,它不起作用,因为每个json.Marshal
被调用,它将运行Object.MarshalJSON
方法,该方法本身调用json.Marshal,导致无限循环。
示例:
package main
import (
"fmt"
"encoding/json"
"sync"
)
type Object struct {
Name string
Value int
sync.RWMutex
}
func (o *Object) MarshalJSON() ([]byte, error) {
o.RLock()
defer o.RUnlock()
fmt.Println("Marshalling object")
return json.Marshal(o)
}
func main() {
o := &Object{Name: "ANisus", Value: 42}
j, err := json.Marshal(o)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", j)
}
输出:
编组对象
编组对象
编组对象
...
显然,在调用json.Marshal之前,我可以删除MarshalJSON方法并在main函数内调用Lock()。但是,我的问题是:
有没有办法在结构的MarshalJSON方法中调用json.Marshal(或者至少让json包处理编码)?
奖金问题
为什么我的程序没有冻结?当第二次递归调用MarshalJSON时,不应该锁定结构吗?
答案 0 :(得分:8)
您可以在递归调用上为别名设置别名。这是Play。
别名类型(JObject)没有定义marshal函数,因此它无法无限递归
package main
import (
"fmt"
"encoding/json"
"sync"
)
type Object struct {
Name string
Value int
sync.RWMutex
}
//Type alias for the recursive call
type JObject Object
func (o *Object) MarshalJSON() ([]byte, error) {
o.RLock()
defer o.RUnlock()
fmt.Println("Marshalling object")
// This works because JObject doesn't have a MarshalJSON function associated with it
return json.Marshal(JObject(*o))
}
func main() {
o := &Object{Name: "ANisus", Value: 42}
j, err := json.Marshal(o)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", j)
}
答案 1 :(得分:1)
简单回答: 由于无限递归,您的程序会冻结。
您调用json.Marshal(o)
会在您的方法中显示MarshalJSON()
,但遗憾的是您还在json.Marshal(o)
中调用了MarshalJSON()
,最终导致无限原因递归并耗尽系统内存
它被称为common rookie mistake因为你的代码会导致无限递归。
以下是使用String()
另一个递归示例:
package main
import "fmt"
type A int
func (a A) String() string {
return fmt.Sprintf("%v", a)
}
func main() {
var a A
fmt.Println("this will never print", a)
}
这就是为什么go试图将impose stack size limit作为时间解决方案
2简单解决方案
return json.Marshal(o)
但是解决方案1示例
package main
import (
"encoding/json"
"fmt"
"sync"
)
type Object struct {
Name string
Value int
sync.RWMutex
}
func (o *Object) ParseJSON() ([]byte, error) {
o.RLock()
defer o.RUnlock()
fmt.Println("Marshalling object")
return json.Marshal(o)
}
func main() {
o := &Object{Name: "ANisus", Value: 42}
j, err := o.ParseJSON() // THis would work
if err != nil {
panic(err)
}
fmt.Printf("%s\n", j)
j, err = json.Marshal(o) // this would work
if err != nil {
panic(err)
}
fmt.Printf("%s\n", j)
}
解决方案2示例
package main
import (
"encoding/json"
"fmt"
"sync"
)
type Item struct {
Name string
Value int
}
type Object struct {
item Item
sync.RWMutex
}
func (o *Object) MarshalJSON() ([]byte, error) {
o.RLock()
defer o.RUnlock()
fmt.Println("Marshalling object")
return json.Marshal(o.item)
}
func main() {
o := &Object{item : Item{Name: "ANisus", Value: 42}}
j, err := json.Marshal(o)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", j)
}