I have a mongoDb instance connected and my request (from my React app) gets a 200 back. I can see the collection in Mondodb as "cats" with a record for each cat but the cat records looks like:
_id:5f66641bad25efa40b810d83 - fine
name:"" - should not be blank
favorite:false -should be true
I had had some cors problems at first so I changed what was "Content-Type" to "content-type" (for Access-Control-Allow-Headers) and that "fixed" the cors issue. I have no idea if that is what is breaking the code though.
Since this is my first time setting up a database on my own I am just not sure where I have broken the code. Any help would be appreciated.
Here is my CreateCat method:
func CreateCat(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Context-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "content-type")
var cat models.Cat
_ = json.NewDecoder(r.Body).Decode(&cat)
fmt.Println(cat)
insertOneFavoriteCat(cat)
json.NewEncoder(w).Encode(cat)
}
Here is my model:
type Cat struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,string"`
Favorite bool `json:"favorite,string"`
}
This is what println prints: { false}
and if you are curious if it's my frontend, this is the request getting sent:
{"cat":{"name":"tabby","favorite":true}}
Problem is that JSON MongoDB returns doesn't match how your struct would be decoded. That false comes from Favorite field which cannot be found in JSON object with cat key returned by MongoDB. To decode this type of response you need to define a wrapping struct (as CatModel in my example below) because your Cat is not top level object but just a member key of upper level JSON object:
type CatModel struct {
Cat struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Name string `json:"name"`
Favorite bool `json:"favorite"`
} `json:"cat"`
}
Also you have typo (Context-Type instead of Content-Type) in header name. Correct Header().Set() is:
w.Header().Set("Content-Type", "application/json; charset=utf-8")
Related
I am using the Kentico-cloud Swift SDK to grab a bunch of elements from the CMS using the Delivery API in the background.
One of the Swift SDK methods allows me to get a ContentType for a certain element on the CMS so I can then map it to an object in my code. Here's the code:
self.client.getContentType(name: codename, completionHandler: { (isSuccess, contentType, error) in
guard error == nil else {
print(error!)
return
}
if isSuccess {
if let type = contentType {
print(type)
self.client.getItem(modelType: type, itemName: codename, completionHandler: { (isSuccess, deliveryItem, error) in
if isSuccess {
// save this Element
print(deliveryItem)
} else {
if let error = error {
print(error)
}
}
})
}
}
})
the attribute codename is the name of the object I am trying to find the ContentType for. The call succeeds and I get my ContentType object, unfortunately, it does not have any properties in it that aren't nil.
I assume it should give me the name of the type as a String so I can then map it to my class.
Could you verify you have valid content type codename in the name parameter? I've tried to reproduce it (see attached screenshot) and everything works on my side (there is also test for this feature which passes as well in GetContentType.swift).
Could you post the value of a requestUrl property from DeliveryClient.swift getContentType() method line 176?
Edit: Oh, from your screen on the GitHub issue I can see you are trying to get the content type with the codename of the item which in wrong. You should use the codename of the content type.
From the docs for getContentType() method:
/**
Gets single content type from Delivery service.
- Parameter name: The codename of a specific content type.
- Parameter completionHandler: A handler which is called after completetion.
- Parameter isSuccess: Result of the action.
- Parameter contentTypes: Received content type response.
- Parameter error: Potential error.
*/
You can learn more about content types here.
I also had the same thought usps tracking but thanks that you have provided a question.
Thanks and Regards,
Shane.
I am working on a project that uses combination of Go and MongoDB. I am stuck at a place where I have a struct like:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
In this struct, the field Id is of int type. But as we know MongoDB's default id is of bsonObject type. Some times, the system generates the default MongoDB id in Id field.
To overcome this I have modified the struct like this:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
BsonId bson.ObjectId `json:"bson_id" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
In above struct, I have mapped the same _id field in two different struct fields Id (type int) and BsonId (type bson.ObjectId). I want if id of integer type comes, it maps under Id otherwise under BsonId.
But this thing is giving following error:
Duplicated key '_id' in struct models.Booking
How can I implement this type of thing with Go Structs ??
Update:
Here is the code I have written for custom marshaling/unmarshaling:
func (booking *Booking) SetBSON(raw bson.Raw) (err error) {
type bsonBooking Booking
if err = raw.Unmarshal((*bsonBooking)(booking)); err != nil {
return
}
booking.BsonId, err = booking.Id
return
}
func (booking *Booking) GetBSON() (interface{}, error) {
booking.Id = Booking.BsonId
type bsonBooking *Booking
return bsonBooking(booking), nil
}
But this is giving type mismatch error of Id and BsonId fields. What should I do now ?
In your original struct you used this tag for the Id field:
bson:"_id,omitempty"
This means if the value of the Id field is 0 (zero value for the int type), then that field will not be sent to MongoDB. But the _id property is mandatory in MongoDB, so in this case the MongoDB server will generate an ObjectId for it.
To overcome this, the easiest is to ensure the Id will is always non-zero; or if the 0 is a valid id, remove the omitempty option from the tag.
If your collection must allow ids of mixed type (int and ObjectId), then the easiest would be to define the Id field with type of interface{} so it can accomodate both (actually all) types of key values:
Id interface{} `json:"_id,omitempty" bson:"_id,omitempty"`
Yes, working with this may be a little more cumbersome (e.g. if you explicitly need the id as int, you need to use type assertion), but this will solve your issue.
If you do need 2 id fields, one with int type and another with ObjectId type, then your only option will be to implement custom BSON marshaling and unmarshaling. This basically means to implement the bson.Getter and / or bson.Setter interfaces (one method for each) on your struct type, in which you may do anything you like to populate your struct or assemble the data to be actually saved / inserted. For details and example, see Accessing MongoDB from Go.
Here's an example how using custom marshaling it may look like:
Leave out the Id and BsonId fields from marshaling (using the bson:"-" tag), and add a 3rd, "temporary" id field:
type Booking struct {
Id int `bson:"-"`
BsonId bson.ObjectId `bson:"-"`
TempId interface{} `bson:"_id"`
// rest of your fields...
}
So whatever id you have in your MongoDB, it will end up in TempId, and only this id field will be sent and saved in MongoDB's _id property.
Use the GetBSON() method to set TempId from the other id fields (whichever is set) before your struct value gets saved / inserted, and use SetBSON() method to "copy" TempId's value to one of the other id fields based on its dynamic type after the document is retrieved from MongoDB:
func (b *Booking) GetBSON() (interface{}, error) {
if b.Id != 0 {
b.TempId = b.Id
} else {
b.TempId = b.BsonId
}
return b, nil
}
func (b *Booking) SetBSON(raw bson.Raw) (err error) {
if err = raw.Unmarshal(b); err != nil {
return
}
if intId, ok := b.TempId.(int); ok {
b.Id = intId
} else bsonId, ok := b.TempId.(bson.ObjectId); ok {
b.BsonId = bsonId
} else {
err = errors.New("invalid or missing id")
}
return
}
Note: if you dislike the TempId field in your Booking struct, you may create a copy of Booking (e.g. tempBooking), and only add TempId into that, and use tempBooking for marshaling / unmarshaling. You may use embedding (tempBooking may embed Booking) so you can even avoid repetition.
Some JSON data I am getting have spaces in the key names. I am using standard encoding/json library to unmarshal the data. However it is unable to understand the keys with spaces in the schema. For e.g. following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var jsonBlob = []byte(`[
{"Na me": "Platypus", "Order": "Monotremata"},
{"Na me": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string `json: "Na me"`
Order string `json: "Order,omitempty"`
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
}
Gives the output as:
[{Name: Order:Monotremata} {Name: Order:Dasyuromorphia}]
So in the schema the library removes the space(from Na me) and try to find the key (Name), which is obviously not present. Any suggestion what can I do here?
Your json tag specification is incorrect, that's why the encoding/json library defaults to the field name which is Name. But since there is no JSON field with "Name" key, Animal.Name will remain its zero value (which is the empty string "").
Unmarshaling Order will still work, because the json package will use the field name if json tag specification is missing (tries with both lower and upper-case). Since the field name is identical to the JSON key, it works without extra JSON tag mapping.
You can't have a space in the tag specification after the colon and before the quotation mark:
type Animal struct {
Name string `json:"Na me"`
Order string `json:"Order,omitempty"`
}
With this simple change, it works (try it on the Go Playground):
[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
var Messages []Token
c2 := session.DB("mydatabase").C("pages")
query2 := c2.Find(bson.M{}).All(&Messages)
fmt.Print(Messages)
Here's the structure in my Mongo DB:
id_
pageUrl
token
pageId
I first tried the structure as this:
type Token struct {
PageUrl string
Token string
PageId string
}
but only the token was being printed, perhaps because it's all lowercase. The other two fields were not being retrieved because they contain uppercase. Then I tried this:
type Token struct {
PageUrl string `json: "pageUrl" bson: "pageUrl"`
Token string `json: "token" bson: "token"`
PageId string `json: "pageId" bson: "pageId"`
}
what are those bson and json things? I've only put it there because I've seen in the internet, but it doesn't work, I still get only the token field
UPDATE with solution and tested example for nested documents
I've seen that there was no posts regarding this question so remember that the solution was to remove the spaces between json: and bson:
Also, to help someone who might be wondering how to do it for nested structs, here I give two structures that worked for me:
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}
type Message struct {
Sender struct {
Id string `json:"id" bson:"id"`
} `json:"sender" bson:"sender"`
Recipient struct {
Id string `json:"id" bson:"id"`
} `json:"recipient" bson:"recipient"`
Message struct {
Mid string `json:"mid" bson:"mid"`
Seq int `json:"seq" bson:"seq"`
Message string `json:"text" bson:"text"`
}
}
these json and bson stuff is called tags
My best guess is that because Go requires a variable or function to be public by Capitalize the first character, so serialize frameworks like json or bson require the struct Capitalize its first character to expose the field(so that it could see the field). Thus the exposed field name should be defined with a tag (to avoid the restriction).
the space between bson: and "token" seems to have cause the problem
I tried following code snippet and seems works fine.
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}
I'm posting the following json string:
{'foods':[{'vName':'bean','color':'green','size':'small'},
{'vName':'carrot','color':'orange', 'size':'medium'}]}
I'm posting to Go using Restangular, and the receiving func is:
func CreateFoods(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var food Food //this needs to be an array or something?
dec := json.NewDecoder(r.Body)
dec.Decode(&food)
}
My Food struct:
type Food struct{
VName string `json:"vName"`
Color string `json:"color"`
Size string `json:"size"`
}
I've used this routine for cases where where I post a single entity, but now I want to post multiple entities, and I cannot figure out how to map this json example to multiple entities.
Also, I'm trying to "see" the JSON POST, to see the JSON string, then if I have to, I can work with the string to make the entities. I cannot figure out how to get a hold of the JSON string from http.Request.
Add this:
// You might use lowercase foods since it is maybe not something you want to export
type Foods struct {
Foods []Food
}
When decoding use this:
var foods Foods
dec.Decode(&foods)
To see the body of a response as a string:
bytes, err := ioutil.ReadAll(r.Body)
fmt.Println(string(bytes))
Small detail: After the last two lines you now read the body contents. You should then decode the json not using json.NewDecoder and Decode but json.Unmarshal. Complete example for CreateFoods() to prevent confusion:
bytes, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("error reading body")
return
}
fmt.Println(string(bytes))
var foods Foods
json.Unmarshal(bytes, &foods)
Hope it works, didn't test, let me know!
The proper way to handle decoding json if you have an io.Reader is to use json.NewDecoder, using ioutil.ReadAll then json.Unmarshal is slow and creates a lot of unneeded buffers.
To handle an array like your json example you need to create a struct like this:
type Foods struct {
Foods []Food `json:"foods"`
}
However if the json is just an array [{'vName':'bean','color':'green','size':'small'} {'vName':'carrot','color':'orange', 'size':'medium'}], you can just use:
type Foods []Food
type Food struct {
VName string `json:"vName"`
Color string `json:"color"`
Size string `json:"size"`
}
func CreateFoods(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var food Foods //this needs to be an array or something?
dec := json.NewDecoder(r.Body)
dec.Decode(&foods)
}