【问题标题】:Custom JSON serialization and deserialization for interfaces in GoGo 中接口的自定义 JSON 序列化和反序列化
【发布时间】:2018-03-09 07:57:10
【问题描述】:

我目前正在为 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
}

我已经尝试了几种解决方案来实现这一点,但没有一个看起来是真正的“正确方法”:

  1. 不关心内容

这似乎是显而易见的解决方案,只需使用一个简单的结构:

type PostSection struct{
  Type    string
  Content interface{}
}

这样,我可以通过任何前端 POSTS 并保存它。但是,操作数据或验证数据变得不可能,所以这不是一个好的解决方案。

  1. 使用自定义接口序列化

我发现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
}

最好是这样,在为我的所有类型实现 MarshalJSONUnmarshalJSON 之后,直接在 PostSection 对象上使用 json.Marshal 时它工作正常。

但是,当序列化或反序列化包含PostSections 数组的整个 Post 对象时,我的自定义代码将被忽略,而 PostSections 将被视为底层对象(stringmap[string]string 在示例)在序列化时,或在反序列化时导致空对象。

  1. 为整个 Post 结构编写自定义序列化

因此,我目前正在使用但想要更改的解决方案是对整个 Post 对象进行自定义序列化。这会导致代码超级丑陋,因为我只需要为单个字段自定义代码,所以我将通过其余部分,使反序列化看起来类似于:

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

然后,像这样解码这些部分:

sections := make([]PostSection, len(decoded.Sections))
for i, s := range decoded.Sections {
    if s["type"] == "text" {
        content := s["content"].(map[string]interface{})
        langs := make(PostText, len(content))
        for lang, langContent := range content {
            langString := langContent.(string)
            langs[lang] = langString
        }
        sections[i] = &langs
    } else if s["type"] == "image" {
        content := s["content"].(string)
        contentString := PostImage(content)
        sections[i] = &contentString
    }
}

p.Sections = sections

每次我想在其他地方(例如在时事通讯中)以另一种形式包含 PostSection 时,我都必须使用大量代码,而且从长远来看,它不像是惯用的 go 代码。此外,格式错误的部分没有错误处理 - 它们只会导致这样的恐慌。

这个问题有干净的解决方案吗?

【问题讨论】:

  • #2,when serializing or deserializing an entire Post object containing an array of PostSections, my custom code was not being used and I would get errors。什么错误?您是否为 Post 对象本身实现了 MarshalJSONUnmarshalJSON
  • 对不起,我好像记错了。我只是再次尝试,并且仅在尝试从 mgo 反序列化时出现错误,因为它找不到 PostSection 接口的SetBSON 方法。至于你的第二个问题,我在#3 中谈到。

标签: json go serialization


【解决方案1】:

为避免为整个Post 编写UnmarshalJSON,您可以将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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多