2017-09-27 50 views
1

我目前正在为golang中的博客开发JSON API,并且遇到试图处理博客帖子的序列化和反序列化的障碍。我希望我的帖子包含可能包含许多内容(如正常段落,图片,引号等)的帖子部分数组。我使用蒙戈存储(与惊人的mgo library),我要保存这样的帖子:Go中的接口的自定义JSON序列化和反序列化

{ 
    "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并保存它。但是,操纵数据或验证数据变得不可能,所以这不是一个好的解决方案。

  • 使用自定义接口系列化
  • 我发现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只想被视为串行化时底层对象(在实施例中stringmap[string]string),或者在反序列化时导致空对象。

    对于整个邮政结构

    整个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代码通过一次远射。此外,没有错误处理格式错误的部分 - 他们只是造成这样的恐慌。

    有没有一个干净的解决这个问题?

  • +1

    #2,'当对包含PostSections数组的整个Post对象进行序列化或反序列化时,我的自定义代码没有被使用,并且我会得到错误。什么错误?你是否为Post对象本身实现了'MarshalJSON'和'UnmarshalJSON'? – RayfenWindspear

    +0

    对不起,但似乎我记得那是错的。我只是试了一遍,错误只出现在试图从mgo反序列化时,因为它找不到PostSection接口的SetBSON方法。至于你的第二个问题,我在#3中谈论这个问题。 – happens

    回答

    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 
    } 
    
    +0

    这很好,非常感谢!现在感觉很明显,呃... – happens

    相关问题