我目前正在golang中为博客开发JSON API,并且我遇到了试图处理博客帖子的序列化和反序列化的障碍。我希望我的帖子包含一系列帖子部分,这些部分可能是很多东西(例如普通段落,图像,引号等)。我使用Mongo进行存储(使用惊人的mgo library)并且我想保存这样的帖子:
{
"title": "Blog post",
"sections": [
{
"type": "text",
"content": { "en": "English content", "de": "Deutscher Inhalt" }
},
{
"type": "image",
"content": "https://dummyimage.com/100x100"
},
...more sections
],
...other fields
}
我已经尝试了几种解决方案来实现这一点,并且没有一个真正看起来像是正确的方式"这样做:
这似乎是一个明显的解决方案,只需使用一个简单的结构:
type PostSection struct{
Type string
Content interface{}
}
这样,我可以通过任何前端POSTS并保存它。但是,操纵数据或验证数据变得不可能,因此它不是一个好的解决方案。
我发现this article关于在golang中序列化接口。起初这看起来很棒,因为我可以有这样的界面:
type PostSection interface{
Type() string
Content() interface{}
}
然后实现这样的每个类型:
type PostImage string
func (p *PostImage) Type() string {
return "image"
}
func (p *PostImage) Content() interface{} {
return p
}
最理想的情况是,在为我的所有类型实现MarshalJSON
和UnmarshalJSON
之后,直接在PostSection对象上使用json.Marshal时,它工作正常。
但是,在序列化或反序列化包含PostSection
数组的整个Post对象时,我的自定义代码被忽略,PostSections只会被视为底层对象(string
或{序列化时的示例中的{1}},或者在反序列化时导致空对象。
因此,我目前正在使用但希望更改的解决方案是整个Post对象的自定义序列化。这导致了超级丑陋的代码,因为我只需要为单个字段定制代码,因此我会通过其余字段,使反序列化看起来与此类似:
map[string]string
然后解码这样的部分:
p.ID = decoded.ID
p.Author = decoded.Author
p.Title = decoded.Title
p.Intro = decoded.Intro
p.Slug = decoded.Slug
p.TitleImage = decoded.TitleImage
p.Images = decoded.Images
...more fields...
这是我每次想在其他地方以其他形式包含PostSections时必须使用的大量代码(例如在时事通讯中),并且它并不像惯用的代码那样远射。此外,对于格式错误的部分没有错误处理 - 它们只会引起这样的恐慌。
这个问题有一个干净的解决方案吗?
答案 0 :(得分:2)
为避免为整个UnmarshalJSON
编写Post
,您可以将PostSection
包装在具体类型中并让它实现Unmarshaler接口。
type Post struct {
ID int
Author string
Title string
Intro string
Slug string
TitleImage string
Images []string
Sections []*PostSection
}
type SectionContent interface {
Type() string
Content() interface{}
}
type PostSection struct {
Content SectionContent
}
func (s *PostSection) UnmarshalJSON(data []byte) error {
// ...
return nil
}