Converting between struct types in Go - go

So I have a question that I've been trying to figure out for a while and hopefully someone has some insight. Basically, I have two similar, but different structs in Go and I would like to convert between them.
Something like the following:
type A struct {
Name string
}
type B struct {
Name NameStruct
}
type NameStruct struct {
Firstname string
Lastname string
}
In this example,
B.Name == NameStruct{
Firstname: strings.Split(A.Name, " ")[0],
Lastname: strings.Split(A.Name, " ")[1],
}
Basically I would like to be able to define a mapping of fields from one struct to the other as well as a conversion function, so that I can do this without writing all of the code to do the conversion manually.
What I'm imagining is that there might be some sort of Go generator out there that could do something like this. I don't think it is possible using reflection because of the nested structs (although I'm open to suggestions).
Basically, does anyone know of a way to do this that is better than either writing conversion code manually or creating my own generator?

Related

Modifying struct

I have a struct such as
type Info struct {
Foo string
FooBar string
Services string
Clown string
}
and lets say I have already populated the first 2 fields
input := &Info{
Foo: "true",
Services: "Massage",
}
Is there a way to "reopen" the struct to add missing elements. Something like this :
input = {
input,
Foobar: "Spaghetti",
Clown: "Carroussel"
}
Instead of
input.Foobar = "Spaghetti"
input.Clown = "Carroussel"
I have multiple fields and just don't really like having many lines input.Fields. I didn't find anything like it. So I was wondering.
No, this is not supported by the language's syntax.
Btw, the solution you want to avoid consists of less lines that the theoretical alternative :) (2 lines vs 4 lines in your example).
One could create a helper function which copies non-zero fields from one instance of a struct to another, so you could create a struct with the additional fields using a composite literal and use that as the source, but this requires using reflection, which is slow, and this solution wouldn't be more readable.
See related: "Merge" fields two structs of same type

Polymorphism on structs without methods in Go

I'm working on several web server projects in Go, and there is a common problem that I'm always facing. I know we can achieve something like polymorphism in Go with interfaces and methods, but many times I had a scenario that I needed polymorphism on some data-holder structs that (maybe) just had some common fields, and no methods at all.
For example consider a story writing platform, where each user can write short stories and novels:
type ShortStory struct {
Name string
ID int
Body string
}
type LongStory struct {
Name string
ID int
Chapters []string
}
Now I simply want to have a data layer function, say GetStories(), which fetches all stories written by a user from database.
func GetStories(id int) []SOME_TYPE {
...
}
There are really no methods that I want to have on my ShortStory and LongStory structs. I know I can add a dummy method and let them satisfy some Storier interface, then use that interface as return type. But since there is no method I would want on a data container model, adding a dummy method just for the language to enable a feature, seems like a poor design choice to me.
I can also make the function return []interface{}, but that's against the whole idea of "typed language" I believe.
Another way is to have two separate GetShortStories() and GetLongStories() methods, which return a slice of their own type. But at some point I would finally want to merge those two slices into one and there I would again need a []interface{}. Yes, I can return a JSON like:
{
"short_stories" : [...],
"long_stories" : [...]
}
But I want my json to be like:
[{...}, {...}, {...}]
And I wouldn't change my APIs because of a language's limits!
I'm not a pro in Go, so am I missing something here? Is there a Go-ish approach to this, or is it really bad language design on Golang's side?
If you cannot express what you want to do using the features of a language, you should first try to change the way you structure your program before blaming the language itself. There are concepts that cannot be expressed in Go but can be expressed well in other languages, and there are concepts you cannot express well in other languages but you can in Go. Change the way you solve the problem to effectively use the language.
One way you can address your problem is using a different type of struct:
type Story struct {
Name string
ID int
ShortBody string
Chapters []string
}
If the Chapters is empty, then it is a short story.
Another way:
type Story struct {
Name string
ID int
Content StoryContent
}
type StoryContent interface {
Type() string
}
type ShortStory interface {
StoryContent
Body() string
}
type LongStory interface {
StoryContent
Chapters() []string
}
etc.

How to convert one struct to another in Go when one includes another?

I would like to know if there is easy way to convert from one struct to another in Go when one struct includes the other.
For example
type Type1 struct {
Field1 int
Field2 string
}
type Type2 struct {
Field1 int
}
I know that it can be handled like this
var a Type1{10, "A"}
var b Type2
b.Field1 = a.Field1
but if there are many fields, I will have to write numerous assignments. Is there any other way to handle it without multiple assignments?
In a word, is there anything like b = _.omit(a, 'Field2') in javascript?
Not directly, no. You can freely convert between identical types only.
You can get various levels of solutions to this type of problem:
writing the assignments out yourself (likely the best performance)
using reflection to copy from one to the other based on field names
something quick-and-dirty like marshalling one type to JSON then unmarshalling to the other type (which is basically using reflection under the hood with a plaintext middleman, so it's even less efficient, but can be done with little work on your part)

Declaring a field tag in proto message

I just dived in Go programming using protobuf and I'm at the point where I need to validate data in a struct. I found govalidator, which seems to do the perfect job for what I need. It does validate structs based on the field tags, something like
type Contact struct {
firstName string `valid:"alpha,required"`
lastName string `valid:"alpha,required"`
email string `valid:"email,required"`
}
jdoe := &Contact{
firstName: "John",
lastName: "Doe",
email: "jdoe#mail.com"
}
ok, err = govalidator.ValidateStruct(jdoe)
And my protobuf definition would look like
message Contact {
string firstName = 1;
string lastName = 2;
string email = 3;
}
Now my question would be, is there a way to define the field tags in the proto message. From what I've seen in the generated go code, the compiler adds tags to the fields anyway, but could I "sneak" the ones that I need too? Also, I would imagine that unmarshalling could be one possible solution, but it somehow seems inefficient to me to unmarshal just to copy the field values to an equivalent struct which would have the necessary field tags.
Having the same structure for the data encapsulation and the input coming from the client was just a pure coincidence. As it has been suggested not only in the comments, but also by co-workers more experienced (than me) with protobuf I've just mapped (1:1 in this particular case) the fields from the structure generated by proto to the data encapsulation structure I have defined.

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,

Resources