douhuang1973 2017-03-10 03:30
浏览 60
已采纳

将任意字段添加到未知结构的json输出中

In this stackoverflow post it's explained how to add arbitrary fields to a golang struct by using it as an anonymous. This works fine if you are working with known struct types, but I'm wondering how to do the same thing when dealing with an unknown struct or interface.

I wrote the following example to demonstrate:

package main

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

type example interface{}
type Data struct {
    Name string
}

func printInterface(val interface{})    {
    example1 := struct {
        example
        Extra string
    }{
        example: val,
        Extra: "text",
    }
    json.NewEncoder(os.Stdout).Encode(example1)
}

func printStructPointer(val *Data)  {
    example2 := struct {
        *Data
        Extra string
    }{
        Data: val,
        Extra: "text",
    }
    json.NewEncoder(os.Stdout).Encode(example2)
}


func main() {
    d := Data{Name:"name"}
    fmt.Println("Example 1:")
    printInterface(&d)
    fmt.Println("Example 2:")
    printStructPointer(&d)
}

This prints the following:

Example 1:
{"example":{"Name":"name"},"Extra":"text"}
Example 2:
{"Name":"name","Extra":"text"}

I'm so assuming that I was working within printInterface how do get the JSON output to look like the JSON output of printStructPointer?

  • 写回答

2条回答 默认 最新

  • dqkkrt8860 2017-03-10 10:05
    关注

    There's an important difference between printInterface() and printStructPointer(). The first one embeds an interface type, while the second embeds a struct type (more specifically a pointer to a struct type).

    When you embed a struct (or pointer to struct) type, the fields of the embedded type get promoted, so in the 2nd example it will be valid to write example2.Name. When you embed an interface type, an interface does not have fields, so no fields will be promoted. So it doesn't matter if the interface value wraps a struct (or pointer to struct), fields of that struct won't get promoted (they can't be).

    Thus, in the printInterface() the interface wrapping a struct won't get "flattened" in the JSON result.

    Solving it with generating a dynamic type using reflection

    One way to solve this is to generate a dynamic type at runtime, using reflection (reflect package). This new type will be a struct, and it will contain an anonymous struct field being of the type that is wrapped in the passed interface, and will also contain our extra field (of type string).

    This is how it could look like:

    func printInterface(val interface{}) {
        t2 := reflect.StructOf([]reflect.StructField{
            reflect.StructField{
                Name: "",
                Type: reflect.TypeOf(val),
            },
            reflect.StructField{
                Name: "Extra",
                Type: reflect.TypeOf(""),
            },
        })
    
        v2 := reflect.New(t2).Elem()
        v2.Field(0).Set(reflect.ValueOf(val))
        v2.FieldByName("Extra").SetString("text")
    
        json.NewEncoder(os.Stdout).Encode(v2.Interface())
    }
    

    Output is as expected (try it on the Go Playground):

    Example 1:
    {"Name":"name","Extra":"text"}
    Example 2:
    {"Name":"name","Extra":"text"}
    

    Solving it with marshaling twice

    Another way would be to marshal the value, unmarshal it into a map, add the extra field and marshal it again:

    func printInterface(val interface{}) error {
        data, err := json.Marshal(val)
        if err != nil {
            return err
        }
    
        v2 := map[string]interface{}{}
        if err := json.Unmarshal(data, &v2); err != nil {
            return err
        }
    
        v2["Extra"] = "text"
        return json.NewEncoder(os.Stdout).Encode(v2)
    }
    

    Output is the same. Try it on the Go Playground.

    This solution is simpler, easier to follow, but it's slower as it marshals twice. Also note that in this example the fields in the result might be in different order, as iteration order on a map is not specified in Go (for details see Why can't Go iterate maps in insertion order?).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料