将任意字段添加到未知结构的json输出 [英] Adding Arbitrary fields to json output of an unknown struct

查看:182
本文介绍了将任意字段添加到未知结构的json输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个 stackoverflow后它解释了如何添加任意字段到一个golang结构通过使用它作为匿名。如果你正在处理已知的结构类型,这工作正常,但我想知道如何在处理未知的结构或接口时做同样的事情。



我写了下面的例子来演示:



<$ p













$ b类型示例interface {}
类型数据结构{
名称字符串
}

func printInterface(val interface {}){
example1:= struct {
示例
额外字符串
} {
例如:val,
Extra:text,
}
数据结构{
示例2:=结构数据{
*数据结构{b $ j $ b $}
额外字符串
} {
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)
}

这会打印以下内容:

 示例1:
{example:{Name:name},Extra:text}
示例2:
{Name name,Extra:text}

我假设我在工作在 printInterface 中如何使JSON输出看起来像 printStructPointer ?的JSON输出$ b $ printInterface()和 printStructPointer()之间有一个重要的区别 / code>。第一个嵌入一个接口类型,而第二个嵌入一个结构类型(更具体地说是一个指向结构类型的指针)。

当你嵌入一个结构体(或指针struct)类型,嵌入类型的字段被提升,所以在第二个例子中写入 example2.Name 是有效的。当您嵌入接口类型时,接口不包含字段,因此不会提升字段。因此,如果接口值封装结构体(或指向结构体的指针)并不重要,则该结构体的字段不会被提升(它们不能)。



<因此,在 printInterface()中,封装结构的接口在JSON结果中不会被扁平化。



使用反射来生成动态类型解决此问题

解决此问题的一种方法是在运行时使用反射生成动态类型( 反映 包)。这个新类型将是一个结构体,它将包含一个匿名结构体字段,该结构体字段被封装在传入的接口中,并且还包含我们的额外字段(类型为 string

这是它的样子:

  func printInterface(val interface {}){
t2:= reflect.StructOf([] reflect.StructField {
reflect.StructField {
名称:,
类型: reflect.TypeOf(val),
},
reflect.StructField {
名称:Extra,
类型: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())
}

输出结果如预期(在开始游戏地面):

 示例1:
{Name:name,Extra :text}
示例2:
{Name:name,Extra:text}



用两次编组解决它



另一种方法是编组价值,将它解组成映射,添加额外字段并重新编组:

  func printInterface(val interface {})错误{
data,err:= json .Marshal(val)
if err!= nil {
return err
}

v2:= map [string] interface {} {}
如果错误:= json.Unmarshal(data,& v2); err!= nil {
return err
}

v2 [Extra] =text
return json.NewEncoder(os.Stdout).Encode v2)
}

输出相同。试试去游乐场



这个解决方案更简单,更容易遵循,但它更慢,因为它收集了两次。另请注意,在本例中,结果中的字段可能以不同的顺序排列,因为Go中未指定地图上的迭代顺序(有关详细信息,请参阅)。


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?

解决方案

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?).

这篇关于将任意字段添加到未知结构的json输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆