如何在Go中为标量派生的类型实现UnmarshalJSON?

I have a simple type that implements conversion of subtyped integer consts to strings and vice versa in Go. I want to be able to automatically unmarshal strings in JSON to values of this type. I can't, because UnmarshalJSON doesn't give me a way to return or modify the scalar value. It's expecting a struct, whose members are set by UnmarshalJSON. The ",string" method doesn't work either for other than builtin scalar types. Is there a way to implement UnmarshalJSON correctly for a derived scalar type?

Here's an example of what I'm after. I want it to print "Hello Ralph" four times, but it prints "Hello Bob" four times because the PersonID isn't being changed.

package main

import (
    "encoding/json"
    "fmt"
)

type PersonID int

const (
    Bob PersonID = iota
    Jane
    Ralph
    Nobody = -1
)

var nameMap = map[string]PersonID{
    "Bob":    Bob,
    "Jane":   Jane,
    "Ralph":  Ralph,
    "Nobody": Nobody,
}

var idMap = map[PersonID]string{
    Bob:    "Bob",
    Jane:   "Jane",
    Ralph:  "Ralph",
    Nobody: "Nobody",
}

func (intValue PersonID) Name() string {
    return idMap[intValue]
}

func Lookup(name string) PersonID {
    return nameMap[name]
}

func (intValue PersonID) UnmarshalJSON(data []byte) error {
    // The following line is not correct
    intValue = Lookup(string(data))
    return nil
}

type MyType struct {
    Person   PersonID `json: "person"`
    Count    int      `json: "count"`
    Greeting string   `json: "greeting"`
}

func main() {
    var m MyType
    if err := json.Unmarshal([]byte(`{"person": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {
        fmt.Println(err)
    } else {
        for i := 0; i < m.Count; i++ {
            fmt.Println(m.Greeting, m.Person.Name())
        }
    }
}

Use a pointer receiver for the unmarshal method. If a value receiver is used, changes to the receiver are lost when the method returns.

The argument to the unmarshal method is JSON text. Unmarshal the JSON text to get a plain string with all JSON quoting removed.

func (intValue *PersonID) UnmarshalJSON(data []byte) error {
  var s string
  if err := json.Unmarshal(data, &s); err != nil {
    return err
  }
  *intValue = Lookup(s)
  return nil
}

There's a mismatch between the JSON tags an the example JSON. I changed the JSON to match the tag, but you can change it the other way.

if err := json.Unmarshal([]byte(`{"person": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {

playground example

Here's an answer based on my comment. I'm not sure this is exactly what you want to do as some of your questions wording confuses me however the basic idea is to separate unmarshalling and transformation into two different steps. First unmarshal raw data into a compatible type, after do a transformation to another type or enrich the type you already have like in the example below. You're welcome to hide this behavior in a custom implementation of UnmarshalJSON if you'd like but I would personally advise against it. Here's my two reasons; 1) it's just not consistent with Go's explicit verbose coding style 2) I despise highly obfuscated packages/libraries/languages that do stuff like this for you because sooner or later it bites you in the ass an costs you a lot more than adding that 1 line of extra code in a few places (like hours trying to debug something that makes no sense to you).

type MyType struct {
    Id   PersonID
    Name     string   `json: "name"` 
    Count    int      `json: "count"`
    Greeting string   `json: "greeting"`
}


func main() {
    var m MyType
    if err := json.Unmarshal([]byte(`{"name": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {
        fmt.Println(err)
    } else {
        m.Id = Lookup(m.Name) // see this isn't unmarshalling
        // better to take the data as is and do transformation separate
        for i := 0; i < m.Count; i++ {
            fmt.Println(m.Greeting, m.Person.Name())
        }
    }
}