编组结构时用嵌入的json替换ObjectId

时间:2015-06-03 04:50:54

标签: json mongodb go

我正在使用Go和MongoDB构建一个RESTful API,并且我遇到了一些困难,即将JSON嵌入到JSON中的一个文档中。这是我想要完成的一个玩具示例。我有以下架构:

type Post struct {
    ID    bson.ObjectId `json:"id,omitempty"`
    Title string        `json:"title,omitempty"`
    Owner bson.ObjectId `json:"owner,omitempty"` // references a User
}

type User struct {
    ID   bson.ObjectId `json:"id,omitempty"`
    Name string        `json:"name,omitempty"`
}

在为帖子创建JSON时,我想首先在MongoDB中查找帖子的所有者,并将结果用户嵌入到帖子的JSON(原始ObjectId的原位)中,就像这样:

{
    "id": "...",
    "title": "My awesome post",
    "owner": {
        "id": "...",
        "name": "Cody"
    }
}

除了使用map[string]interface{}手动构建JSON之外,我不太确定如何完成此操作,如下所示:

post := LookupPost(...)
user := LookupUser(post.Owner)

m := map[string]interface{}{
    "id": post.ID,
    "title": post.Title,
    "owner": map[string]interface{}{
        "id": user.ID,
        "name": user.Name,
    },
}

b, _ := json.Marshal(m)

显然这个不能很好地扩展不是很干 - 理想情况下,我可以在每个结构定义中使用json标签并插入字段自动。

我错过了什么,或者我正在尝试做什么不可能?或者我只是不正确地在Go中接近MongoDB / JSON?为了正确看待问题,我来自Node.js背景,这种功能很简单。

修改

为了澄清事情,这里有一些不正确的Go代码,显示了我想要做的事情

func getPostJSON() []byte {
    p := LookupPost(...)
    u := LookupUser(p.Owner, ...)

    uj, _ := json.Marshal(u)
    p.Owner = uj // can't do this in Go

    pj, _ := json.Marshal(p)

    return pj
}

2 个答案:

答案 0 :(得分:1)

我不熟悉MongoDB或bson.ObjectId,但您可以将自己的类型替换为User字段,并让MongoDB轻松地为用户填写该字段bson.ObjectId

如果是这样,你可以将用户对象id包装成他们自己的实现json.Marshaler接口的类型。 E.g:

// Embedded (instead of `type x bson.ObjectId`) so that we
// get all the methods and satisfy all the interfaces that
// bson.ObjectId does. Hopefully that's engough to allow MongoDB
// to fill in fields of this type from a database??
type ownerObjID struct{ bson.ObjectId }

// Here we marshal the results of looking up the user from the id
// rather than just the ID itself.
func (oid ownerObjID) MarshalJSON() ([]byte, error) {
    user, err := LookupUser(oid.ObjectId)
    if err != nil {
        return nil, err
    }
    return json.Marshal(user)
}

type Post struct {
    ID    bson.ObjectId `json:"id,omitempty"`
    Title string        `json:"title,omitempty"`
    Owner ownerObjID    `json:"owner,omitempty"` // <-- is this type wrapping doable/easy with MongoDB?
}

type User struct {
    ID   bson.ObjectId `json:"id,omitempty"`
    Name string        `json:"name,omitempty"`
}

func main() {
    post := LookupPost()
    b, err := json.MarshalIndent(post, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("JSON:\n%s\n", b)
}

// Some stubs for demo:
func LookupPost() Post {
    return Post{
        ID:    "postID001",
        Title: "Ima Test",
        Owner: ownerObjID{"ownerID002"},
    }
}

func LookupUser(id bson.ObjectId) (User, error) {
    return User{
        ID:   id,
        Name: "name for " + string(id),
    }, nil
}

Playground

给我:

JSON:
{
  "id": "postID001",
  "title": "Ima Test",
  "owner": {
    "id": "ownerID002",
    "name": "name for ownerID002"
  }
}

答案 1 :(得分:0)

所以我实际上发现了一个更清洁的解决方案:

type Post struct {
    ID bson.ObjectId `bson:"_id,omitempty" json:"id,omitempty"`
    Title string `bson:"title,omitempty" json:"title,omitempty"`
    Owner UserRef `bson:"owner,omitempty" json:"owner,omitempty"`
}

type User struct {
    ID   bson.ObjectId `json:"id,omitempty"`
    Name string        `json:"name,omitempty"`
}

type UserRef bson.ObjectId

func (ref UserRef) GetBSON() (interface{}, error) {
    return bson.ObjectId(ref), nil
}

func (ref UserRef) MarshalJSON() ([]byte, error) {
    u := LookupUserInMongoDB(ref)

    return json.Marshal(u)
}

以下是它的工作原理 - 在将Post转换为bson时,mgo无法将UserRef存储为ObjectId,因此我们可以为UserRef实现GetBSON方法以返回基础ObjectId。这允许我们将Owner作为ObjectId存储在数据库中。并且,与@ DaveC的答案一样,我们为UserRef实现MarshalJSON方法,以便在将Post转换为json时,我们可以将ObjectId替换为实际的嵌入式用户。