使用自定义MarshalJSON更改结构中的JSON标签

We get some JSON input, unmarshal, perform some work, then marshal and ship off somewhere else. The JSON we get may have a field named "user". When we marshal back to JSON we need to have that field "user" changed to "username". We can do this by creating a new struct with all the same fields, but different JSON tags, but that seemed a bit cumbersome. I thought a custom marshaller would work here, but I'm a bit stuck. Consider the following code.

package main

import (
    "encoding/json"
    "fmt"
)

type StructA struct {
    Username string `json:"user"`
    Process  string `json:"process"`
}

func main() {

    var test1 StructA
    err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
    if err != nil {
        fmt.Println(err)
    }

    // do some work with test1

    jsonByte, err := json.Marshal(&test1)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(string(jsonByte))

}

func (u *StructA) MarshalJSON() ([]byte, error) {
    type Alias StructA
    return json.Marshal(&struct {
        Username string `json:"username"`
        *Alias
    }{
        Username: u.Username,
        Alias:    (*Alias)(u),
    })
}

https://play.golang.org/p/_w0rlQrcgrW

Ideally this would allow me to change the JSON tag on that field from "user" to "username". However, I get both "username" and "user".

{"username":"user123","user":"user123","process":"something"}

I certainly could create an entirely new struct that mirrors StructA with the tags I want, but I don't have to have to copy every single field and worry about keeping those two structs in sync. It's not the end of the world, but it doesn't seem like a good approach.

To be clear, the end result I'm looking for is the following:

{"username":"user123","process":"something"}

I'm sure I'm missing something trivial here, but it's been a long week and any assistance would be appreciated. Thanks!

One option could be to have one struct with the non-changing values and than 2 alternative structs which both include that struct and have only the changing values. You then use one for unmarshaling and the second one for marshaling.

type StructA struct {
    Process  string `json:"process"`
    ...
}

type WithUser struct {
    StructA
    Username `json:"user"`
}

type WithUsername struct {
    StructA
    Username `json:"username"`
}

This would require multiple structs but no duplication in each one and can be quite flexible in what you include, instead of hard coding what you want to change into a custom marshal function.

use reflect to create struct and change it's tag

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type StructA struct {
    Username string `json:"user"`
    Process  string `json:"process"`
}

func main() {

    var test1 StructA
    err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
    if err != nil {
        fmt.Println(err)
    }

    // do some work with test1

    jsonByte, err := json.Marshal(&test1)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(string(jsonByte))

}

func (u *StructA) MarshalJSON() ([]byte, error) {
    // get old struct fields
    uType := reflect.TypeOf(u).Elem()
    userNameField, _ := uType.FieldByName("Username")
    // set username field tag
    userNameField.Tag = `json:"username"`
    processField, _ := uType.FieldByName("Process")
    newType := reflect.StructOf([]reflect.StructField{userNameField, processField})
    // set new value field
    oldValue := reflect.ValueOf(u).Elem()
    newtValue := reflect.New(newType).Elem()
    for i := 0; i < oldValue.NumField(); i++ {
        newtValue.Field(i).Set(oldValue.Field(i))
    }
    return json.Marshal(newtValue.Interface())
}