在Go语言中实现对嵌套结构的JSON编组器。

huangapple go评论81阅读模式
英文:

Implementing json marshaller over embedded stuct in Go

问题

我有一个结构体,我想要高效地将其转换为 JSON:

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

这个结构体包含了已知形式的元数据和未知形式的 Contents。Contents 列表在运行时被填充,所以我对它们没有完全的控制。为了提高 Go 的编组速度,我想要在 Meta 结构体上实现 Marshaller 接口。Marshaller 接口的定义如下:

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

请注意,Meta 结构体并不像这里展示的那么简单。我尝试在 Meta 结构体上实现 Marshaler 接口,但是当我对 MyStruct 进行 JSON 编组时,结果只包含了 Meta 编组接口返回的结果。

所以我的问题是:我如何对一个包含了具有自己的 JSON 编组器的嵌入结构体和另一个没有编组器的结构体的结构体进行 JSON 编组?

英文:

I have a struct that I would like to efficiently JSON encode:

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

The struct contains meta data of a known form and Contents of an unknown form, The contents list is populated during runtime, so I don't really have control over them. To improve Go's marshalling speed, I would like to implement the Marshaller interface over the Meta struct. The Marshaller interface looks like this:

type Marshaler interface {
        MarshalJSON() ([]byte, error)
}

Please keep in mind that the Meta struct is not as simple as shown here. I've tried implementing the Marshaler interface over the Meta struct, but it seems that when I then JSON marshal MyStruct, the result is only the result returned by the Meta marshalling interface.

So my question is: How can I JSON marshal a struct, that contains en embedded struct with its own JSON marshaller and another struct without one?

答案1

得分: 4

由于匿名字段*MetaMarshalJSON方法将被提升到MyStruct中,当MyStruct被编组时,encoding/json包将使用该方法。

你可以这样做,而不是让Meta实现Marshaller接口,你可以在MyStruct上实现该接口,代码如下:

package main

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

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

func (m *MyStruct) MarshalJSON() ([]byte, error) {
	// 在这里对 Meta 进行编组
	meta := `"Id":` + strconv.Itoa(m.Meta.Id)

	// 手动调用 Contents 的编组
	cont, err := json.Marshal(m.Contents)
	if err != nil {
		return nil, err
	}
    
	// 将它们拼接在一起
	return []byte(`{` + meta + `,"Contents":` + string(cont) + `}`), nil
}


func main() {
	str := &MyStruct{&Meta{Id:42}, []interface{}{"MyForm", 12}}

	o, err := json.Marshal(str)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(o))
}

输出结果为:{"Id":42,"Contents":["MyForm",12]}

Playground

英文:

Since the MarshalJSON method of the anonymous field *Meta will be promoted to MyStruct, the encoding/json package will use that method when MyStruct will be marshalled.

What you can do is, instead of letting Meta implement the Marshaller interface, you can implement the interface on MyStruct like this:

package main

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

type MyStruct struct {
    *Meta
    Contents []interface{}
}

type Meta struct {
    Id int
}

func (m *MyStruct) MarshalJSON() ([]byte, error) {
	// Here you do the marshalling of Meta
	meta := `"Id":` + strconv.Itoa(m.Meta.Id)

	// Manually calling Marshal for Contents
	cont, err := json.Marshal(m.Contents)
	if err != nil {
		return nil, err
	}
    
	// Stitching it all together
	return []byte(`{` + meta + `,"Contents":` + string(cont) + `}`), nil
}


func main() {
	str := &MyStruct{&Meta{Id:42}, []interface{}{"MyForm", 12}}

	o, err := json.Marshal(str)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(o))
}

>{"Id":42,"Contents":["MyForm",12]}

Playground

huangapple
  • 本文由 发表于 2013年8月30日 16:02:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/18527887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定