Empty or not required struct fields
Asked Answered
B

2

61

I have two structs that represent models that will be inserted into a mongodb database. One struct (Investment) has the other struct (Group) as one of its fields.

type Group struct {
    Base
    Name string `json:"name" bson"name"`
}

type Investment struct {
    Base
    Symbol string `json:"symbol" bson:"symbol" binding:"required"`
    Group  Group  `json:"group" bson:"group"`
    Fields bson.M `json:"fields" bson:"fields"`
}

The problem I'm having is that in the Investment model, Group is not required. If there is no group, I think its better for it to not be inserted in the db. Whats the best way to handle a db model such as this in Go?

Bisexual answered 14/6, 2014 at 4:14 Comment(2)
This is not my area of expertise, but you may want to investigate json.Marshaler golang.org/pkg/encoding/json/#Marshaler it lets you define custom Marshaling behavior for json, which should (with some effort) allow you to not write a given field based on some logic.Birthright
Struct tags: json: "omitempty" should do the trick, from memory.Mauriciomaurie
S
80

tl;dr: Use ,omitempty, and if you need to worry about the difference between a zero value and null/not specified, do what the GitHub API does and use a pointer.


Both json and bson support the ,omitempty tag. For json, "empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero" (json docs). For bson, ,omitempty means "Only include the field if it's not set to the zero value for the type or to empty slices or maps", and zero values include empty strings and nil pointers (bson docs).

So if you really need a Group struct, you can put a *Group in instead, and it won't be stored when the pointer is nil. If Investment only needs to hold the group's name, it's even simpler: "" as group name keeps a group key from being stored.

bson defaults to using the lowercased field name already so you can omit that from the struct tag when they match. json will default to the Capitalized name, so specify the lowercase name in a tag if you need lowercase.

So, best case, maybe you can just use:

type Investment struct {
    Base
    Symbol string `json:"symbol" binding:"required"`
    Group string  `json:"group,omitempty" bson:",omitempty"`
    Fields bson.M `json:"fields"`
}

If you ever run into fields where the zero value for the type ("", 0, false, etc.) is distinct from "not specified", you can do what the GitHub API does and put pointers in your structures--essentially an extension of the *Group trick.

Swiger answered 14/6, 2014 at 6:4 Comment(3)
Not a Mongo user so correct me if it's wrong. Had read GitHub's post about pointers for omittable fields and the comment suggesting ,omitempty, and dug up the relevant docs, and looks like it'll work.Swiger
You don't need to specify the lowercase name in a tag for JSON fields. As per the docs: To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match.Fortepiano
If you're only decoding you don't have to worry about case, but if you want to Marshal/Encode and have lowercase names appear in the JSON, you have to use the struct tags to ask for it.Swiger
D
7

Avoid strut fields to marshal if they are empty -

A struct field may be primitive type(string, int, bool etc) or even an another struct type.

So sometimes we don't want a struct's field to go in json data(may to database insertion or in external api call) if they are empty

Example:

type Investment struct {
    Base
    Symbol string `json:"symbol" bson:"symbol" binding:"required"`
    Group  Group  `json:"group" bson:"group"`
    Fields bson.M `json:"fields" bson:"fields"`
}

If we want that Symbol and Group might contain empty values(0, false, nil pointer, zero size interface/struct) then we can avoid them in json marshaling like below.

type Investment struct {
    Base
    Symbol string `json:"symbol,omitempty" bson:"symbol,omitempty" binding:"required"`
    Group  *Group  `json:"group,omitempty" bson:"group,omitempty"`
    Fields bson.M `json:"fields" bson:"fields"`
}

Her "Group" field is pointer to Group struct and whenever it will point to nil pointer it will be omitted from json marshaling.

And obviously we would be filling values in Group field like below.

// declared investment variable of type Investment struct

 investment.Group = &groupData
Denten answered 24/6, 2020 at 3:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.