I am using Golang/Fiber Mongo driver.
I have simple struct for blog post:
type Post struct {
ID primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
Title *string `json:"title" bson:"title"`
Slug *string `json:"slug" bson:"slug"`
Content []interface{} `json:"content" bson:"content,omitempty"` // same as any[]
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
PublishedAt *time.Time `json:"published_at" bson:"published_at"`
}
In content I put array of objects:
[
{
data: 'blah blah'
},
{
data: 'blah blah'
}
]
Method itself is pretty straightforward:
func GetPostBySlug(slug string) (Post, error) {
post := Post{}
filter := bson.M{
"slug": slug,
}
database, error := Database.GetMongoDatabase()
if error != nil {
println(error)
}
collection := database.Collection(model_name)
err := collection.FindOne(ctx, filter).Decode(&post)
if err != nil {
return post, err
}
return post, nil
}
And this is what I get:
"_id": "000000000000000000000000",
"title": "Test",
"slug": "test",
"content": [ // array of arrays? what??
[
{
"Key": "data",
"Value": "blah blah"
},
{
"Key": "data",
"Value": "blah blah"
},
]
],
"created_at": "2022-06-07T21:08:04.261Z",
"updated_at": "2022-07-20T21:42:36.717Z",
"published_at": null
In mongodb the content field is saved exactly as I passed it, but when I am trying to get the document, the content transforms to this weird array of arrays with key-value pairs.
If I save something like:
content: [
{
data: {
test: 'test',
what: 'what'
}
}
]
It will transform to this:
content: [
[
Key: "data",
Value: [
{
Key: "test",
Value: "test"
},
{
Key: "what",
Value: "what"
},
]
]
]
I understand the reasons behind this (this is just golang's way to handle JSON?) and assume there should be an extra step somewhere in the middle, but I have no idea what exactly I need to do
CodePudding user response:
Modify type of Content
to interface{}
or just string
.
CodePudding user response:
The reason for what you see is because you use interface{}
for the element type of Content
:
Content []interface{}
If you use interface{}
, it basically carries no type information what type the driver should use when umarshaling the array elements, so the driver will choose / use bson.D
to represent the documents of the content
field. bson.D
is a slice that holds the ordered list of fields of the documents, that's why you see an "array of arrays". Each bson.D
is a slice, representing a document.
type D []E
type E struct {
Key string
Value interface{}
}
If you can model the array elements with a struct, use that, e.g.:
type Foo struct {
Bar string
Baz int
}
Content []Foo `json:"content" bson:"content,omitempty"`
// Or a pointer to Foo:
Content []*Foo `json:"content" bson:"content,omitempty"`
If you don't have a fixed model for the array elements, alternatively you may use bson.M
which is a map (but the fields / properties will be unordered which may or may not be a problem):
type M map[string]interface{}
Using it:
Content []bson.M `json:"content" bson:"content,omitempty"`