Пользовательский MarshalJSON() никогда не вызывается в Go

Я написал собственные версии MarshalJSON и UnmarshalJSON. Мой UnmarshalJSON вызывается так, как я хочу, но я не могу заставить его работать с MarshalJSON. Вот код, который суммирует мою проблему:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "os"
)

type myStruct struct {
    Data string `json:"data"`
}

func (s *myStruct) MarshalJSON() ([]byte, error) {
    return []byte(`{"data":"charlie"}`), nil
}

func (s *myStruct) UnmarshalJSON(b []byte) error {
    // Insert the string directly into the Data member
    return json.Unmarshal(b, &s.Data)
}

func main() {
    // Create a struct with initial content "alpha"
    ms := myStruct{"alpha"}

    // Replace content with "bravo" using custom UnmarshalJSON() (SUCCESSFUL)
    if err := json.NewDecoder(bytes.NewBufferString(`"bravo"`)).Decode(&ms); err != nil {
        log.Fatal(err)
    }

    // Use custom MarshalJSON() to get "charlie" back (UNSUCCESSFUL)
    if err := json.NewEncoder(os.Stdout).Encode(ms); err != nil {
        log.Fatal(err)
    }

    // Trying another method (UNSUCCESSFUL)
    if ret, err := json.Marshal(ms); err != nil {
        log.Fatal(err)
    } else {
        fmt.Println(string(ret))
    }

    // Verify that the Marshaler interface is correctly implemented
    var marsh json.Marshaler
    marsh = &ms
    ret, _ := marsh.MarshalJSON()
    fmt.Println(string(ret)) // Prints "charlie"
}

Короче говоря, программа кодирует struct "автоматически" двумя способами, а затем, наконец, вызывает MarshalJSON вручную. Ответ, который я хочу, это "charlie". Запуск кода генерирует следующий вывод:

{"data":"bravo"}
{"data":"bravo"}
{"data":"charlie"}

Попробуйте на Go Playground: http://play.golang.org/p/SJ05S8rAYN.


person Anders Sjöqvist    schedule 27.01.2014    source источник


Ответы (1)


В этой части кода ms копируется в переменную interface{}:

// Trying another method (UNSUCCESSFUL)
if ret, err := json.Marshal(ms); err != nil {

Проблема в том, что эта переменная не реализует интерфейс json.Marshaler, поскольку MarshalJSON не входит в набор методов. для myStruct (только для *myStruct).

Исправление состоит в том, чтобы либо (а) заставить ваш метод MarshalJSON принимать получатель без указателя (что будет означать, что он получает копию структуры: возможно, дорого, если она большая), либо (б) маршалировать указатель на структуру (как Каву упоминается в комментарии).

Причина такого поведения в том, что Go не позволяет вам получить указатель на значение, хранящееся внутри переменной интерфейса, вместо этого требует, чтобы вы делали копию значения всякий раз, когда вы хотите получить к нему доступ. Хотя в языке есть синтаксический сахар для преобразования ms.MarshalJSON() в (&ms).MarshalJSON() как способ доступа к методу с приемником указателя, это невозможно сделать для значения, хранящегося в переменной интерфейса. По этой причине метод не считается включенным в свой набор методов.

person James Henstridge    schedule 28.01.2014
comment
очень поучительно, &myStruct{} это спасательный шаблон - person Valerio; 23.05.2014