Home > Software design >  Is this example of method overriding in Go?
Is this example of method overriding in Go?

Time:05-11

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strings"
)

type Animal int

const (
    Unknown Animal = iota
    Gopher
    Zebra
)

func (a *Animal) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }
    switch strings.ToLower(s) {
    default:
        *a = Unknown
    case "gopher":
        *a = Gopher
    case "zebra":
        *a = Zebra
    }

    return nil
}

func (a Animal) MarshalJSON() ([]byte, error) {
    var s string
    switch a {
    default:
        s = "unknown"
    case Gopher:
        s = "gopher"
    case Zebra:
        s = "zebra"
    }

    return json.Marshal(s)
}

func main() {
    blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]`
    var zoo []Animal
    if err := json.Unmarshal([]byte(blob), &zoo); err != nil {
        log.Fatal(err)
    }

    census := make(map[Animal]int)
    for _, animal := range zoo {
        census[animal]  = 1
    }

    fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras:  %d\n* Unknown: %d\n",
        census[Gopher], census[Zebra], census[Unknown])

}

This is the code snippet of Json custom marshal example in go doc. My question is where is the call to MarshalJSON and UnmarshalJSON method in this code. Are these method somehow overriding Json package's UnmarshalJSON and MarshalJSON method. I thought go does not support method overriding this way. Pls help, i am not able to understand what is happening in this code!!

CodePudding user response:

The documentation says:

To unmarshal JSON into a value implementing the Unmarshaler interface, Unmarshal calls that value's UnmarshalJSON method, including when the input is a JSON null.

Somewhere in the json.Unmarshal implementation, there's code similar this:

 u, ok := v.(Unmarshaler)
 if ok {
     err := u.Unmarshal(data)
     if err != nil { /* handle error */}
 } else {
     // handle other kinds of values
 }

The code uses a type assertion to determine if the value satisfies the json.Unmarshaler interface. If the value does satisfy the method, the value's UnmarshalJSON function is called.

The (*Animal).UnmarshalJSON function is called because *Animal satisfies the json.Unmarshaler interface.

CodePudding user response:

This is an example of implementing an interface from a different package.

There's no need to explicitly declare that you're implementing an interface in Go, like there is in Java or C for example. You just have to implement all the functions it declares. In this case, you're implementing the Unmarshaler interface declared in the json package which is used by the Unmarshal function.

CodePudding user response:

The methods are defined on the type itself. When you call json.Marshal / json.Unmarshal and it finds the methods on your type, it will use them.

From the docs about json.Marshal:

Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON. If no MarshalJSON method is present but the value implements encoding.TextMarshaler instead, Marshal calls its MarshalText method and encodes the result as a JSON string. The nil pointer exception is not strictly necessary but mimics a similar, necessary exception in the behavior of UnmarshalJSON.

And respectively from the docs about json.Unmarshal:

To unmarshal JSON into a value implementing the Unmarshaler interface, Unmarshal calls that value's UnmarshalJSON method, including when the input is a JSON null. Otherwise, if the value implements encoding.TextUnmarshaler and the input is a JSON quoted string, Unmarshal calls that value's UnmarshalText method with the unquoted form of the string.

  •  Tags:  
  • go
  • Related