Home > Mobile >  Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value
Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value

Time:10-01

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 with null/nil value on pointer type
  • how we can call UnmarshalJSON/MarshalJSON everytime when data is null/nil and type is a pointer instead of implementing UnmarshalJSON/MarshalJSON on A type and modify b property from level of A

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

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
}
  •  Tags:  
  • go
  • Related