2015-06-03 17 views
2

我使用Go和MongoDB構建了一個RESTful API,並且遇到了一些困難,將一個文檔的JSON嵌入另一個文檔中。這是我想要完成的一個玩具例子。我有以下模式:編組時使用嵌入的json替換ObjectId struct

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標記並自動插入字段。

我是否錯過了一些東西,或者是我想要做的不可能?或者我只是沒有正確接近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 
} 
+0

_「顯然這不能很好地擴展」_你是否想要擺脫遞歸調用?或者您是否在尋找某種訪問底層數據庫的代理對象以根據需要檢索數據? –

+0

@SylvainLeroux對不起,不明確!我的意思是說它在代碼的複雜性方面不能很好地擴展。我主要關心的是保持乾爽 - 對於我的項目結構(這需要很長的時間來描述),我爲什麼要避免手動拼接json,這會帶來一些影響。 – Cody

+0

我把'bson:'_ id,omitempty'json:「id,omitempty」'放在字符串字段中,並且像魅力一樣工作。 – Mohsen

回答

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時,不能將UserRef存儲爲ObjectId,因此我們可以實現用於UserRef返回底層ObjectId的方法。這允許我們將Owner作爲ObjectId存儲在數據庫中。和@DaveC的答案一樣,我們爲UserRef實現MarshalJSON方法,以便在將Post轉換爲json時,我們可以用實際的嵌入式用戶替換ObjectId。

1

我不familar與MongoDB的或bson.ObjectId什麼,但您可以替換您自己的User字段的類型,並讓MongoDB輕鬆地從用戶的bson.ObjectId中爲您填充?

如果是這樣,您可以將用戶對象標識符換成它們自己的類型來實現json.Marshaler接口。例如:

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

非常感謝你,這正是我一直在尋找的!儘管爲了將來的參考,我必須做一些調整才能使它與MongoDB兼容 - 接下來你需要添加一個'bson:「,'inline''標籤所有者ownerObjID'和一個'bson:」owner''標籤。到'struct ownerObjID'中的'bson.ObjectId'。這將以名稱「owner」正確存儲MongoDB中的ObjectId。 – Cody