Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value
I'm trying to implement custom UnmarshalJSON
and MarshalJSON
on pointer type, however none of this function is called when data from json is null/nil
like in example below:
package main
import (
"encoding/json"
"fmt"
)
type A struct {
B *B `json:"b,omitempty"`
}
type B int
// Only for displaying value instead of
// pointer address when calling `fmt.Println`
func (b *B) String() string {
if b == nil {
return "nil"
}
return fmt.Sprintf("%d", *b)
}
// This function is not triggered when json
// data contains null instead of number value
func (b *B) UnmarshalJSON(data []byte) error {
fmt.Println("UnmarshalJSON on B called")
var value int
if err := json.Unmarshal(data, &value); err != nil {
return err
}
if value == 7 {
*b = B(3)
}
return nil
}
// This function is not triggered when `B`
// is pointer type and has `nil` value
func (b *B) MarshalJSON() ([]byte, error) {
fmt.Println("MarshalJSON on B called")
if b == nil {
return json.Marshal(0)
}
if *b == 3 {
return json.Marshal(7)
}
return json.Marshal(*b)
}
func main() {
var a A
// this won't call `UnmarshalJSON`
json.Unmarshal([]byte(`{ "b": null }`), &a)
fmt.Printf("a: % v\n", a)
// this won't call `MarshalJSON`
b, _ := json.Marshal(a)
fmt.Printf("b: %s\n\n", string(b))
// this would call `UnmarshalJSON`
json.Unmarshal([]byte(`{ "b": 7 }`), &a)
fmt.Printf("a: % v\n", a)
// this would call `MarshalJSON`
b, _ = json.Marshal(a)
fmt.Printf("b: %s\n\n", string(b))
}
output:
a: {B:nil}
b: {}
UnmarshalJSON on B called
a: {B:3}
MarshalJSON on B called
b: {"b":7}
My questions are:
- why
UnmarshalJSON/MarshalJSON
is not called withnull/nil
value on pointer type - how we can call
UnmarshalJSON/MarshalJSON
everytime when data isnull/nil
and type is a pointer instead of implementingUnmarshalJSON/MarshalJSON
onA
type and modifyb
property from level ofA
CodePudding user response:
For Short
Currently, unmarshaling/marshaling a Go struct will emit only the non-zero fields, since nil pointer
is one zero value in Go, the UnmarshalJSON/MarshalJSON
is not called in this case.
Also, it seems there are some related proposals
- proposal: encoding/json: add omitzero option
- proposal: encoding/json: allow returning nil from MarshalJSON to omit the field
However, there is no solution to resolve it now.
Per code Unmarshalers
Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op
// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}