【问题标题】:golang json serialization/deserialization of recursive data model递归数据模型的golang json序列化/反序列化
【发布时间】:2016-10-27 07:09:49
【问题描述】:

假设我有:

type IObject interface {
}

type Item struct {
    Description string
    Data        []byte
}

type FunctionX1 struct {
    Object IInclusionObject
}

type FunctionX2 struct {
    Object1 IInclusionObject
    Object2 IInclusionObject
}

我希望能够对模型进行序列化/反序列化,其中ItemFunctionX1FunctionX2 它们都实现了IObject,并且它们可以任意深度地相互指向。

请注意,我不希望:FunctionX1{Item{"foo", []byte("bar")}} 被序列化为:

"object": {
    "Description": "foo"
    "Data": ...
}

而是:

"FunctionX1": {
    "item": { 
        "Description": "foo"
        "Data": ...
    }
}

我需要自己做 JSON 编组器吗 - 似乎我不能使用现有的。

如果我需要自己的相关问题。是否有 JSON 美化器,我可以在其中流式传输有效但随机格式化的 JSON,并输出漂亮的版本(注意 JSON 可能非常大 - 我不想生成,解析生成格式化)。

【问题讨论】:

    标签: json serialization go


    【解决方案1】:

    使用包装器(结构或映射)

    您可以使用struct tags 将结构字段映射到 JSON 中/来自 JSON 的不同名称。因此,您可以将"Object" 更改为"item"

    type FunctionX1 struct {
        Object IInclusionObject `json:"item"`
    }
    

    但如果您希望"FunctionX1" 出现在 JSON 文本中,您仍然需要一个包装器结构或映射。例如:

    f := FunctionX1{Item{"foo", []byte("bar")}}
    
    if data, err := json.Marshal(map[string]interface{}{"FunctionX1": f}); err != nil {
        panic(err)
    } else {
        fmt.Println(string(data))
    }
    

    输出:

    {"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
    

    或者使用包装结构:

    type Wrapper struct {
        FunctionX1 FunctionX1
    }
    
    f := FunctionX1{Item{"foo", []byte("bar")}}
    
    if data, err := json.Marshal(Wrapper{f}); err != nil {
        panic(err)
    } else {
        fmt.Println(string(data))
    }
    

    输出是一样的:

    {"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
    

    如果你想要漂亮的 JSON 格式,你可以使用 json.MarshalIndent() 来进行编组:

    if data, err := json.MarshalIndent(Wrapper{f}, "", "  "); err != nil {
        panic(err)
    } else {
        fmt.Println(string(data))
    }
    

    输出:

    {
      "FunctionX1": {
        "item": {
          "Description": "foo",
          "Data": "YmFy"
        }
      }
    }
    

    尝试Go Playground 上的所有示例。

    使用自定义编组

    如果您不想使用包装器结构或映射,则需要使用自定义编组,但这很简单:

    type FunctionX1 struct {
        Object IInclusionObject `json:"item"`
    }
    
    func (f FunctionX1) MarshalJSON() ([]byte, error) {
        type FunctionX1_ FunctionX1
        return json.Marshal(map[string]interface{}{"FunctionX1": FunctionX1_(f)})
    }
    

    实际上,我们将包装移动到 MarshalJSON() 方法中,因此其他 FunctionX1 的封送值不必这样做。

    测试它:

    f := FunctionX1{Item{"foo", []byte("bar")}}
    
    if data, err := json.Marshal(f); err != nil {
        panic(err)
    } else {
        fmt.Println(string(data))
    }
    
    if data, err := json.MarshalIndent(f, "", "  "); err != nil {
        panic(err)
    } else {
        fmt.Println(string(data))
    }
    

    请注意,MarshalJSON() 中的新 FunctionX1_ 类型是为了避免无限“递归”。

    输出:

    {"FunctionX1":{"item":{"Description":"foo","Data":"YmFy"}}}
    {
      "FunctionX1": {
        "item": {
          "Description": "foo",
          "Data": "YmFy"
        }
      }
    }
    

    Go Playground 上试试这个。

    【讨论】:

    • 谢谢,@icza。自定义封送处理似乎非常接近我想要的,但有一个例外。作为 FinctionX1 参数的对象可能不是一个项目,而是另一个 FunctionX1 或 FunctionX2,在这种情况下,不应将子项目称为项目。我也尝试将相同的自定义编组器应用于项目,但我在那里获得了一个额外的级别:play.golang.org/p/Op2tg1LOtZ。有什么办法可以摆脱它?
    • 其实我想我想通了play.golang.org/p/6lVxnjnyit
    猜你喜欢
    • 1970-01-01
    • 2015-03-17
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 2018-08-05
    • 2022-01-06
    相关资源
    最近更新 更多