如何动态更改struct的json标记?

10 go

我有以下内容:

package main

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

type User struct {
    ID   int64  `json:"id"`
    Name string `json:"first"` // want to change this to `json:"name"`
    tag  string `json:"-"`
    Another
}

type Another struct {
    Address string `json:"address"`
}

func (u *User) MarshalJSON() ([]byte, error) {
    value := reflect.ValueOf(*u)
    for i := 0; i < value.NumField(); i++ {
        tag := value.Type().Field(i).Tag.Get("json")
        field := value.Field(i)
        fmt.Println(tag, field)
    }
    return json.Marshal(u)
}

func main() {
        anoth := Another{"123 Jennings Street"}
    _ = json.NewEncoder(os.Stdout).Encode(
        &User{1, "Ken Jennings", "name",
             anoth},
    )
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试对结构进行json编码,但在此之前我需要更改json密钥...例如,最终的json应该如下所示:

{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
Run Code Online (Sandbox Code Playgroud)

我注意到value.Type().Field(i).Tag.Get("json")的方法,但是没有setter方法.为什么?以及如何获得所需的json输出.

另外,如何遍历所有字段,包括嵌入式结构另一个?

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

Kav*_*ian 11

在Go 1.8中,您可以使用更简单的解决方案.这段代码:

func main() {
    anoth := Another{"123 Jennings Street"}

    _ = json.NewEncoder(os.Stdout).Encode(
        &User{1, "Ken Jennings", "name",
            anoth},
    )
}

type User struct {
    ID   int64  `json:"id"`
    Name string `json:"first"` // want to change this to `json:"name"`
    tag  string `json:"-"`
    Another
}

type Another struct {
    Address string `json:"address"`
}

func (u *User) MarshalJSON() ([]byte, error) {
    type alias struct {
        ID   int64  `json:"id"`
        Name string `json:"name"`
        tag  string `json:"-"`
        Another
    }
    var a alias = alias(*u)
    return json.Marshal(&a)
}
Run Code Online (Sandbox Code Playgroud)

会给我们:

{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
Run Code Online (Sandbox Code Playgroud)

这个解决方案可以通过以下事实实现:在Go 1.8中,您可以为结构分配相同但结构不同的结构.如您所见,type alias与字段具有相同的字段,User但具有不同的标记.

  • 这不是动态的。这仍然是静态编写结构。 (2认同)

mur*_*uru 6

它是kludgy,但是如果你可以将结构包装在另一个结构中,并使用新结构进行编码,那么你可以:

  1. 编码原始结构,
  2. 将其解码为a interface{}以获取地图
  3. 替换地图密钥
  4. 然后编码地图并将其返回

从而:

type MyUser struct {
    U User
}

func (u MyUser) MarshalJSON() ([]byte, error) {
    // encode the original
    m, _ := json.Marshal(u.U)

    // decode it back to get a map
    var a interface{}
    json.Unmarshal(m, &a)
    b := a.(map[string]interface{})

    // Replace the map key
    b["name"] = b["first"]
    delete(b, "first")

    // Return encoding of the map
    return json.Marshal(b)
}
Run Code Online (Sandbox Code Playgroud)

在操场上:https://play.golang.org/p/TabSga4i17


小智 6

您可以使用reflect.StructOfreflect.Value.Convert函数创建一个带有新标签名称的结构副本
https://play.golang.org/p/zJ2GLreYpl0

func (u *User) MarshalJSON() ([]byte, error) {
    value := reflect.ValueOf(*u)
    t := value.Type()
    sf := make([]reflect.StructField, 0)
    for i := 0; i < t.NumField(); i++ {
        fmt.Println(t.Field(i).Tag)
        sf = append(sf, t.Field(i))
        if t.Field(i).Name == "Name" {
            sf[i].Tag = `json:"name"`
        }
    }
    newType := reflect.StructOf(sf)
    newValue := value.Convert(newType)
    return json.Marshal(newValue.Interface())
}
Run Code Online (Sandbox Code Playgroud)