MarshalJSON тип со встроенным типом заканчивается как {} вместо значения - PullRequest
1 голос
/ 25 марта 2019

Чтобы взаимодействовать с чванством, мне нужно было создать пользовательскую структуру BigInt, которая ничего не делает, кроме как обернуться вокруг big.Int.

type BigInt struct {
    big.Int
}

...
type SpendTx struct {
    SenderID    string       `json:"sender_id,omitempty"`
    RecipientID string       `json:"recipient_id,omitempty"`
    Amount      utils.BigInt `json:"amount,omitempty"`
    Fee         utils.BigInt `json:"fee,omitempty"`
    Payload     string       `json:"payload,omitempty"`
    TTL         uint64       `json:"ttl,omitempty"`
    Nonce       uint64       `json:"nonce,omitempty"`
}

func (t SpendTx) JSON() (output []byte, err error) {
    return json.Marshal(t)
}

Я бы ожидал, что SpendTx.JSON() в конце концов вызовет big.Int.MarshalJSON(), что вернет 0. Вместо этого я получил этот вывод:

{"sender_id":"alice","recipient_id":"bob","amount":{},"fee":{},"payload":"Hello World","ttl":10,"nonce":1}

Но я действительно хочу вот что:

{"sender_id":"alice","recipient_id":"bob","amount":10,"fee":10,"payload":"Hello World","ttl":10,"nonce":1}

И мне пришлось добавить этот бит кода в BigInt, чтобы сделать это:

func (b BigInt) MarshalJSON() ([]byte, error) {
    return b.Int.MarshalJSON()
}

Но в соответствии с разделом Effective Go по встраиванию структур , это вообще не нужно. Почему big.Int выглядит как {}?

1 Ответ

6 голосов
/ 25 марта 2019

big.Int реализует пользовательский маршалер JSON (json.Marshaler), см. Int.MarshalJSON().Но у этого метода есть получатель указателя, поэтому он используется / вызывается, только если у вас есть значение указателя: *big.Int.

И вы встраиваете не указательное значение, поэтому этот пользовательский маршалер не вызывается, ипоскольку big.Int является структурой с неэкспортированными полями, в выводе вы увидите пустой объект JSON: {}.

Чтобы заставить его работать, вы должны использовать указатель на ваш тип, например:

Amount      *utils.BigInt `json:"amount,omitempty"`
Fee         *utils.BigInt `json:"fee,omitempty"`

Пример использования:

s := SpendTx{
    SenderID:    "alice",
    RecipientID: "bob",
    Amount:      &utils.BigInt{},
    Fee:         &utils.BigInt{},
}
data, err := s.JSON()
fmt.Println(string(data), err)

Тогда будет вывод, например (попробуйте на Go Playground ):

{"sender_id":"alice","recipient_id":"bob","amount":0,"fee":0} <nil>

Другой вариант - использовать не указатель utils.BigInt, но тогда utils.BigInt должен встроить указатель типа:

type BigInt struct {
    *big.Int
}

type SpendTx struct {
    Amount      utils.BigInt `json:"amount,omitempty"`
    Fee         utils.BigInt `json:"fee,omitempty"`
}

, а затем использовать его:

s := SpendTx{
    SenderID:    "alice",
    RecipientID: "bob",
    Amount:      utils.BigInt{new(big.Int)},
    Fee:         utils.BigInt{new(big.Int)},
}
data, err := s.JSON()
fmt.Println(string(data), err)

И вывод будетбыть снова (попробуйте на Go Playground ):

{"sender_id":"alice","recipient_id":"bob","amount":0,"fee":0} <nil>
...