2013-08-17 2 views
4

Я хотел добавить RLock/RUnlock в структуру, когда он был настроен на json.
В приведенном ниже примере показано, что я пытаюсь сделать. Однако это не работает, потому что вызывается каждый json.Marshal, он будет запускать метод Object.MarshalJSON, который сам по себе вызывает json.Marshal, вызывая бесконечный цикл.Блокировка объекта во время json.Marshal в Go

Пример:

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

Example in Playground

Выход:

Ранжирование Объект
сортировочных Объект
сортировочных Объект
...

Очевидно, что я могу удалить метод MarshalJSON и вызвать функцию Lock() внутри основной функции перед вызовом json.Marshal. Но, мой вопрос скорее:

Есть ли способ вызвать json.Marshal (или, по крайней мере, пакет json обрабатывать кодировку) в методе MarshalJSON структуры?

Бонус вопрос
Почему моя программа не замерзает? Не следует ли блокировать структуру, когда маршал JSON называется рекурсивно второй раз?

+1

почему вы хотите, чтобы заблокировать объект? – akira

+0

@Akira: Если у меня есть несколько goroutines, совместно использующих объект, я хочу, чтобы другие подпрограммы не меняли данные во время их кодирования. В моем примере у меня этого нет, но этот пример не предназначен для отображения фактического использования. – ANisus

+0

в зависимости от размера объекта для кодирования: копирование структуры является допустимым вариантом, imho. – akira

ответ

7

Вы можете псевдоним типа на рекурсивный вызов. Здесь он находится на Play.

алиас типа (JObject) не имеет функцию маршала определены, так что это не бесконечно рекурсивной

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

Perfect. Решение alias делает именно то, что я пытался достичь. Он работает при использовании типа в вложенных структурах и не требует каких-либо изменений в моей структуре. Благодаря! – ANisus

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

Вот почему идут пытаются impose stack size limit как временное решение

2 Простых решений

  • Используйте другое имя
  • Не возвращайте return json.Marshal(o) но Item

Раствор 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) 
} 

Live Demo

Раствор 2 Пример

ра ckage основной

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

Live Demo

+0

Я пошел с решением Дэвида Budworth, потому что он был наиболее близок к решению, о котором я просил. Но +1 для хороших обходных решений, чтобы избежать проблемы рекурсии. – ANisus

Смежные вопросы