0

I defined a "role" type which is a uint64 and use its high 24-bit value to store user roles, and the lower 40-bit are reserved for user-id. My problem now is the custom json marshaler will cause stack overflow. Full code below:

type Role uint64

const (
    RoleAdmin Role = (1 << 40) << iota
    RoleProjectManager
    RoleOperator
    RoleDeveloper
)

var roleNames map[Role]string

func (r Role) Is(a Role) bool {
    return r&a != 0
}

func (r Role) Values() []map[string]any {
    rv := []map[string]any{}
    for m, n := range roleNames {
        if r.Is(m) {
            rv = append(rv, map[string]any{"id": m, "name": n})
        }
    }
    return rv
}

func (r Role) MarshalJSON() ([]byte, error) {
    return json.Marshal(r.Values())
}

func init() {
    roleNames = map[Role]string{
        RoleAdmin:          "administrator",
        RoleProjectManager: "project manager",
        RoleOperator:       "operator",
        RoleDeveloper:      "developer",
    }
}

If I change the marshaler to a pointer receiver, like this:

func (r *Role) MarshalJSON() ([]byte, error) {
    return json.Marshal(r.Values())
}

It will work fine. As far as I understand, unlike UnmarshalJSON(), there is no need to use pointer receiver for MarshalJSON().

xrfang
  • 1,754
  • 4
  • 18
  • 36
  • See [Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow](https://stackoverflow.com/questions/43176625/call-json-unmarshal-inside-unmarshaljson-function-without-causing-stack-overflow/43178272#43178272) – icza Aug 23 '23 at 07:06
  • 5
    Inside the map slice returned by `Values` the `"id"` key's values, i.e. `m`, are of type `Role`, which implements `MarshalJSON`, hence the infinite recursion. To fix, do `"id": int64(m)`. – mkopriva Aug 23 '23 at 07:06

0 Answers0