Подтвердить что ты не робот

Пользовательский маршал JSON() никогда не будет вызван в 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

4b9b3361

Ответ 1

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

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

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

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

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