Embedded structs get all tags and set values for all fields based on tag - go

I'm designing config component where I want to set ENV variable for each field on go struct.
e.g.
type SubStruct struct {
Field int `env:"ENV_INT"`
}
type Config struct {
Name string `env:"APP_NAME"`
Sub1 struct {
Sub11 struct {
Value string `env:"ENV_VAR"`
}
Sub12 SubStruct
}
}
What I want to achieve is to:
Get values of all env keys recursively
Set value based on field type
So there are basically two questions:
Is there any lib in go which exposes methods for setting values using reflection (e.g. like private methods in json package)?
Is there any method/function in go which can parse your struct recursively and get all tags from struct?
edit:
- updated structs
- renamed confusing field names (from embed to sub)

Related

Can I allow arbitrary fields in a shared struct?

I would like to define an object in a shared libary,
type Common struct {
field_a string
custom interface{}
}
where custom is expected to hold additional fields that the user of this common object might define in their file, e.g.,
// module-1
type Mod1Customs struct {
abc string
}
From here, I would like to be able to set Common.custom to Mod1Customs so that I can access both field_a and abc in the same way from within module-1. I would like to do the same thing for other modules that may define an entirely different struct to assign to custom though.
The general idea is to create an object of pre-defined defaults and allowing a bucket of sorts for the users of the object to add their own custom fields.
Is this possible?
What you seem to be looking for is embedding:
type Common struct {
FieldA string
}
type Mod1Customs struct {
Common
Abc string
}
Then Mod1Customs gets access to all of Common fields directly:
c := Mod1Customs{}
fmt.Println(c.FieldA)
fmt.Println(c.Abc)
See Embedding in Effective Go for more details.
If you have some functions that deal with Common fields (no matter what the struct), then you would implement that with an interface that declares the fields you need. Mod1Customs would automatically conform to that interface.

Convert interface{} to struct in Golang

I am very new to Go and am trying to get my head around all the different types and how to use them. I have an interface with the following (which was originally in a json file):
[map[item:electricity transform:{fuelType}] map[transform:{fuelType} item:gas]]
and I have the following struct
type urlTransform struct {
item string
transform string
}
I have no idea how to get the interface data into the struct; I'm sure this is really stupid, but I have been trying all day. Any help would be greatly appreciated.
Decode the JSON directly to types you want instead of decoding to an interface{}.
Declare types that match the structure of your JSON data. Use structs for JSON objects and slices for JSON arrays:
type transform struct {
// not enough information in question to fill this in.
}
type urlTransform struct {
Item string
Transform transform
}
var transforms []urlTransform
The field names must be exported (start with uppercase letter).
Unmarshal the JSON to the declared value:
err := json.Unmarshal(data, &transforms)
or
err := json.NewDecoder(reader).Decode(&transforms)
From your response : [map[item:electricity transform:{fuelType}] map[transform:{fuelType} item:gas]].
As you can see here this is a an array that has map in it.
One way to get the value from this is :
values := yourResponse[0].(map[string]interface{}). // convert first index to map that has interface value.
transform := urlTransform{}
transform.Item = values["item"].(string) // convert the item value to string
transform.Transform = values["transform"].(string)
//and so on...
as you can see from the code above I'm getting the the value using map. And convert the value to the appropriate type in this case is string.
You can convert it to appropriate type like int or bool or other type. but this approach is painful as you need to get the value one bye one and assign it your your field struct.

Access all fields from parent method

I'm developing an application where data is stored in mongodb. There are several collections and of course all of them have some common fields (like Id, creation date, etc) and methods (for example Insert). In my vision, I need to create base model struct with needed fields and methods, and then embed this struct into my models. Unfortunately, this doesn't work because method defined for base model doesn't see child fields.
I don't know how to explain further. Here is code in playground:
https://play.golang.org/p/_x-B78g4TV
It uses json instead of mgo, but idea is still the same.
I want the output to be:
Saving to 'my_model_collection'
{"_id":42, "foo": "Some value for foo", "bar": "Here we set some value for bar"}
Not:
Saving to 'my_model_collection'
{"_id":42}
Writing that insert method for each my model seems to be against DRY, so what is correct/idiomatic way to achieve this in Go?
This is not possible, for details see my answer: Can embedded struct method have knowledge of parent/child?
You may do 2 things:
1. Abandon method and make it a helper / utility function
The idea is to make Insert() detached from BaseModel and make it a simple function, and you pass the document to it which you want to save.
I personally prefer this option, as it requires less hassle and maintenance. It could look like this:
func Insert(doc interface{}) {
j, _ := json.Marshal(doc)
fmt.Println(string(j))
}
You also had a "typo" in the tags:
type MyModel struct {
*BaseModel
Foo string `json:"foo"`
Bar string `json:"bar"`
}
Using it:
Insert(m)
Output (try it on the Go Playground):
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}
2. Pass the (pointer to) the wrapper to the BaseModel
In this approach, you have to pass a pointer to the embedder struct so the BaseModel.Insert() method will have a pointer to it, and may use that to save / marshal. This is basically manually maintaining a "reference" to the struct that embeds us and is being saved/marshalled.
This is how it could look like:
type BaseModel struct {
Id int `json:"_id"`
collectionName string
wrapper interface{}
}
And then in the Insert() method save the wrapper:
func (m *BaseModel) Insert() {
fmt.Printf("Saving to '%v'\n", m.collectionName)
j, _ := json.Marshal(m.wrapper)
fmt.Println(string(j))
}
Creation is slightly more complex:
func NewMyModel() *MyModel {
mm := &MyModel{
Foo: "Some value for foo",
}
mm.BaseModel = NewBaseModel("my_model_collection", mm)
return mm
}
But output is as you wish:
Saving to 'my_model_collection'
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}
Try it on the Go Playground.
In Golang, you can't override a parent method, because that's not how polymorphism works. The Insert method will apply on the BaseModel member, and not on MyModel.
Also, you're trying to use mgo in an improper way. If you want to insert documents in collections, then you already have an Insert method for a Collection struct which works on interface{} types (same as json.Marshal).
Of course, you can have a BaseModel that will contain fields shared by all of your models. In fact, GORM uses a similar approach and provides a Model struct to be included in every child model.
Well known problem ;o) Member variables (like collectionName) which name starts with lower letter are not visible from other packages (like json). Therefore change struct to:
type BaseModel struct {
Id int `json:"_id"`
CollectionName string `json:"collectionName"`
}
and world will be better place to live in.

Conversion of Go struct fields with tags

I am really stuck here with a seemingly trivial problem in Go:
I have a Golang microservice, which outputs data in json format. Let's say I have a simple struct with json tags for this result:
type Result struct {
Name string `json:"name"`
Age int `json:"age"`
}
In the code part where the data is actually pulled out of the database, I have a quite similar struct, like this:
type ResultBackend struct {
Name string `bson:"fullName"`
Age int `bson:"age"`
}
The struct fields are similar, except the different tags. I would like to keep things simple, and return just one struct from the backend service (ResultBackend), which can then be send as a JSON response, like this:
func process() Result {
var result ResultBackend
... do a MongoDB query here and store results in result variable ...
return result
}
This certainly would not work, because we have two different structs here.
Of course one solution would be to embed both tags in one struct like so:
type Result struct {
Name string `json:"name" bson:"fullName"`
Age int `json:"age bson:"age"`
}
and then use this struct in the main code and the "process" function.
This works, but this seems like "poisoning" the Result struct of the main code with the bson tags. What, for instance, if the backend result is a XML file? I'd have to add xml tags to the struct as well. Or maybe someday tags some very obsure database adapter.
This doesn't seem to be the cleanest approach in my eyes. I'd rather have a clean Result struct in the main code, and simply to a conversion from one struct to another.
Is there any easy way doing that, or do I really have to copy all the fields of a ResultBackend struct to a new Result struct and return that? Or I am trying to over-simplify my code here? :)
Cheers!
What I'd do is create a separate type for every "serialisation format" if you will. This approach has several advantages:
Your concerns are separated.
JSON un/marshalling doesn't interfere with BSON/XML, so you can add any kind of additional struct fields (like xml.Name for example).
You can actually create several such types for different APIs that need different parameters.
The only disadvantage I see is that it's more code and even more code to move data between those. Ultimately it's up to you and your application design. If you're sure that your JSON/BSON will stay the same, you can use one type. Otherwise, I'd recommend specialisation.
To those who want to add bson tags in golang struct, here is a tool which can be helpful. I have spent a whole day searching for this and I want others to save their valuable time.
You can convert the following struct
type Result struct {
Name string `json:"name"`
Age int `json:"age"`
}
into
type Result struct {
Name string `json:"name" bson:"fullName"`
Age int `json:"age bson:"age"`
}
With this tool, you can not only add bson tags but also
XML tags,
Define validation and scope
Format tag values
Apply transformation
skip unexported fields.
Go Modify Tags: https://github.com/fatih/gomodifytags
Cheers,

Empty or not required struct fields

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?
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.
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

Resources