繁体   English   中英

Go中接口的自定义JSON序列化和反序列化

[英]Custom JSON serialization and deserialization for interfaces in Go

我目前正在为golang中的博客开发JSON API,并且遇到了处理博客文章的序列化和反序列化的障碍。 我希望我的帖子包含一系​​列帖子部分,这些部分可以是很多东西(例如普通段落,图像,引号等)。 我正在使用Mongo进行存储(带有令人惊叹的mgo库 ),我想保存这样的帖子:

{
  "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. 使用自定义接口序列化

我发现这篇关于在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时,它运行良好。

但是,当对包含PostSection数组的整个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

每当我想在其他地方(例如在新闻通讯中)以另一种形式包含PostSections时,都必须使用大量的代码,而且从长远来看,它并不像惯用的go代码。 另外,对于格式错误的部分也没有错误处理-它们只会引起类似的恐慌。

有没有解决这个问题的解决方案?

为了避免为整个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
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM