Go 将带有嵌入式接口的结构转换为JSON

Go 将带有嵌入式接口的结构转换为JSON,go,Go,我有一个要封送到JSON的结构。它有一个名为Foo(导出为Foo)的已定义字段和一个data接口字段,我想将一个带有附加JSON字段的动态结构传递给该字段 但是,当数据字段是接口而不是特定结构时,它永远不会导出为JSON。我怎样才能做到这一点 package main import ( "encoding/json" "fmt" ) type data interface{} type foo struct { Foo string `json:"foo,omite

我有一个要封送到JSON的结构。它有一个名为
Foo
(导出为
Foo
)的已定义字段和一个
data
接口字段,我想将一个带有附加JSON字段的动态结构传递给该字段

但是,当数据字段是接口而不是特定结构时,它永远不会导出为JSON。我怎样才能做到这一点

package main

import (
    "encoding/json"
    "fmt"
)

type data interface{}

type foo struct {
    Foo string `json:"foo,omitempty"`
    data
}

type bar struct {
    Bar string `json:"bar,omitempty"`
}

func main() {
    b := bar{"bar"}
    f := foo{"foo", b}

    byt, err := json.Marshal(f)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(byt))
}
我需要输出如下所示(它需要是平面的,而不是嵌套的):

2种选择:

  • 设置为it type
    json.RawMessage
    ,这样它就不会被自动解码并保留为一个接口

  • 在结构上编写自定义解组器


  • 您可以通过自定义实现和一点字节切片来实现这一点

    func (f foo) MarshalJSON() ([]byte, error) {
        type goo foo
        g := goo(f)
    
        b1, err := json.Marshal(g)
        if err != nil {
            return nil, err
        }
    
        b2, err := json.Marshal(g.data)
        if err != nil {
            return nil, err
        }
    
        s1 := string(b1[:len(b1)-1])
        s2 := string(b2[1:])
    
        return []byte(s1 + ", " + s2), nil
    }
    


    请注意,这不是检查字节是否可以被切分,也不考虑代码< >数据< /代码>字段是片断或数组的可能情况,我不确定无论如何您希望它扁平化。

    < p>我会编写一个自定义封送器,像这样:

    func (f foo) MarshalJSON() ([]byte, error) {
        type tmp foo
        g := tmp(f)
        first, err := json.Marshal(g)
        if err != nil {
            return nil, err
        }
        second, err := json.Marshal(f.data)
        if err != nil {
            return nil, err
        }
        data := make(map[string]interface{})
        json.Unmarshal(first, &data)
        json.Unmarshal(second, &data)
        return json.Marshal(data)
        //{"bar":"bar","foo":"foo"}
    }
    

    我怀疑你能。最有可能的情况是,您必须对数据本身进行编码并将其插入其余部分。@Volker您能用一个示例代码片段来说明您的想法吗?有两个选项:一个主要问题是,
    data
    是未报告的类型,这意味着
    数据
    嵌入字段未报告。JSON包不处理未报告的成员。它必须导出才能被封送,尽管这仍然不能解决您的平面json需求(它封送为
    {“foo”:“foo”,“Data”:{“bar”:“bar”}
    ),但反射包(json用来封送)的一个轻微的奇怪之处是,可以访问嵌入式未报告结构的导出字段,但嵌入的未报告接口不是。奇怪,但这就是它的工作方式。你能解释一下为什么需要
    typegoo-foo
    ,为什么在
    f
    本身上运行相同的代码会导致堆栈溢出吗?
    typegoo-foo
    定义了一个新的类型
    goo
    ,它的结构与
    foo
    相同,但只有结构。这意味着
    foo
    的方法在
    goo
    上不可用,包括调用者
    MarshalJSON
    ,而字段
    foo
    data
    不可用。因此,
    json.marshall(g)
    将不会调用
    goo.MarshalJSON
    ,因为它不存在,从而避免了堆栈溢出。因此,如果不声明新类型,调用
    json.Marshal(f)
    ,而
    foo.MarshalJSON
    本身也调用
    json.Marshal(f)
    ,将导致无限递归。因此,基本上,
    类型goo foo
    的唯一目的是避免堆栈溢出。谢谢,现在一切都有意义了!
    func (f foo) MarshalJSON() ([]byte, error) {
        type tmp foo
        g := tmp(f)
        first, err := json.Marshal(g)
        if err != nil {
            return nil, err
        }
        second, err := json.Marshal(f.data)
        if err != nil {
            return nil, err
        }
        data := make(map[string]interface{})
        json.Unmarshal(first, &data)
        json.Unmarshal(second, &data)
        return json.Marshal(data)
        //{"bar":"bar","foo":"foo"}
    }