在Go中封装结构

I am new to Go. I have read that encapsulation in Go is on the package level. I have a simple web controller use case. I have a struct which comes in as a JSON object and is Unmarshaled into the struct type.

type User struct{
    Name String `json:"name"`
    //Other Variables
}

Now a json can be unmarshaled into type User Struct by json.Unmarshal([]byte). However, this User struct is available to other packages too. How do I make sure that only methods related to User are accessible by other packages.

One solution I could think of :

type User struct{
    name String
}

type UserJSON struct{
    Name String `json:"name"`
}

func DecodeJSONToUser(rawJSON []byte) (User,error) {
    var userJSON UserJSON
    err := json.Unmarshal(rawJSON,&userJSON)
    //Do error handling
    return User{name:userJSON.Name},nil
}

Is there a GOish way to achieve this ?

You can use a package local struct with a public field so that this struct will not be visible outside the package. Then you can make this struct satisfy some public interface and you have your perfect decoupling:

package user

import "encoding/json"

type User interface {
    Name() string
}

type user struct {
    Username string `json:"name"`
}

func (u *user) Name() string {
    return "Mr. " + u.Username
}

func ParseUserData(data []byte) (User, error) {
    user := &user{}
    if err := json.Unmarshal(data, user); err != nil {
        return nil, err
    }
    return user, nil
}

And the corresponding test:

package user_test

import (
    "testing"

    "github.com/teris-io/user"
)

func TestParseUserData(t *testing.T) {
    data := []byte("{\"name\": \"Uncle Sam\"}")
    expected := "Mr. Uncle Sam"
    if usr, err := user.ParseUserData(data); err != nil {
        t.Fatal(err.Error())
    } else if usr.Name() != expected {
        t.Fatalf("expected %s, found %s", expected, usr.Name())
    }
}

➜ user git:(master) ✗ go test github.com/teris-io/user

ok github.com/teris-io/user 0.001s

You can also convert your package local object to some public object after unmarshaling.

Note: one of the comments mentions how pity it is that due to name clashes (field user.Name on the struct, and method User.Name on the interface) the interface needs to have a different method name. This is not necessary and the code above has been amended correspondingly: the field on the internal structure can have a different name from that in JSON, the corresponding annotation defines the mapping.