7

我想在将结构编组为 json 时将 RLock/RUnlock 添加到结构中。
下面的示例显示了我尝试做的事情。但是,它不起作用,因为everyjson.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)
}

操场上的例子

输出:

编组对象
编组对象
编组对象
...

显然,我可以删除 MarshalJSON 方法并在 main 函数中调用 Lock(),然后再调用 json.Marshal。但是,我的问题是:

有没有办法在结构的 MarshalJSON 方法中调用 json.Marshal (或者至少让 json 包处理编码)?

奖金问题
为什么我的程序没有冻结?第二次递归调用 MarshalJSON 时,不应该锁定结构吗?

4

2 回答 2

13

您可以为递归调用的类型设置别名。这是在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)
}
于 2013-08-17T12:13:58.993 回答
1

简单的答案: 您的程序由于无限递归而冻结。

您调用 json.Marshal(o)which 会查看MarshalJSON()您的方法,但不幸的是您还调用json.Marshal(o)MarshalJSON()which 最终导致无限原因递归并耗尽系统内存

它被称为常见的菜鸟错误,因为您的代码会导致无限递归。

这是您的代码的更简单版本,使用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 试图 将堆栈大小限制作为临时解决方案

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 示例

包主

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)
}

现场演示

于 2013-08-17T10:04:29.790 回答