I am trying to modify some stuff in Go. I have the following struct I created
I have an API call returning something like this
MyStruct struct {
DID string `bson:"d_id" json:"d_id"`
PID string `bson:"p_id" json:"p_id"`
...
}
in one call in the code, I want to append a new key to that struct
like
myNewStruct {
DID string `bson:"d_id" json:"d_id"`
PID string `bson:"p_id" json:"p_id"`
...
MyNewKey string `bson:"new_key" json:"new_key"`
}
The thing is, I want to add a new key, and keep the rest at the root of the object, like the object above. without having to rewrite the full object structure, or doing a for loop of each key.
type MyNewStruct struct {
*MyStruct
MyNewKey MyValueType
}
I have, two variable with the data,
MyStructData and MyNewKeyData
I want to, but don t know how to merge those two data inside MyNewStruct so that everything in MyStructData will be at the root of the key, and everything in MyNewKeyData will be indise the key MyNewKey
I am trying stuff like
theObjectIWant := MyNewStruct {
MyStructData,
"MyNewKey" : MyNewKeyData
}
but doesn't work
When you create an anonymous member in a struct, the compiler generates a name for the member that is named the same as the type. You can use this name when initializing the containing struct.
Here is a simplified example
type MyStruct struct {
DID string
PID string
}
type MyNewStruct struct {
MyStruct
MyNewKey string
}
ms := MyStruct{
DID: "did",
PID: "pid",
}
m := MyNewStruct{
MyStruct: ms,
MyNewKey: "test",
}
I'm not sure I fully understand what your looking for, but this may help.
type MyStruct struct {
DID string
PID string
}
type myNewStruct struct {
MyStruct
newKey string
}
func main() {
s1 := MyStruct{DID: `bson:"d_id" json:"d_id"`}
s2 := myNewStruct{MyStruct: s1, newKey: `bson:"new_key" json:"new_key"`}
fmt.Println(s2.DID)
fmt.Println(s2.newKey)
}
Related
Lets say I have a struct like this (dummy example) :
type Animal struct {
name string
weight int
isLandAnimal boolean
Family
}
type Family struct {
name string
type string
}
Now what I want to achieve is something like this :
var a Animal = ....
bys, err := json.Marshal(a, []string{"name", "Family.name"})
and once printed bys should look like this :
{
"name": "Lion"
"family" : {
"name" : "Felines"
}
}
So I can pass a string slice that references which fields I want to actually get marshaled in the final string.
Does something like this exists in the standard library or in a third party one?
NOTE: This is what I could do based on another SO answer:
https://go.dev/play/p/XVb83zoXpmb
Will this bring troubles down the road?
You can annotate the fields which let you specify how it should show up in json and if it needs to be omitted when empty. https://pkg.go.dev/encoding/json#Marshal
Here is a working code. https://pkg.go.dev/encoding/json#Marshal
package main
import (
"encoding/json"
"fmt"
)
type Animal struct {
Name string `json:"name,omitempty"`
Weight int `json:"weight,omitempty"`
IsLandAnimal bool `json:"IsLandAnimal,omitempty"`
Family Family `json:"Family,omitempty"`
}
type Family struct {
Name string `json:"Name,omitempty"`
Familytype string `json:"Familytype,omitempty"`
}
func main() {
a := Animal{Family: Family{Name: "Felines"}, Name: "Lion"}
bys, _ := json.Marshal(a)
fmt.Println(string(bys))
}
What you're looking for is struct field annotations for json.Marshal. Basically go expects you to explicitly define the mapping from a struct to your export object, in this case json.
You need to ensure that the fields in your struct are exported, or basically start with a capital letter.
Next you can add a annotation to the end of the field to tell go what to look for when unmarshalling, or how to marshal that specific field into the file type format you're looking for.
For example:
This struct with exported members and annotations:
type Cookie struct {
Name string `json:"name"`
Ingredients int `json:"ingredients"`
MilkDunkTime int `json:"milk_dunk_time"`
}
would export into this json:
{
"name": "",
"ingredients: 0,
"milk_dunk_time:" 0
}
I've provided a playground link for your example above with some minor tweaks.
I am trying to create some API docs programmatically, I have this:
type APIDoc struct {
Route string
ResolutionValue struct {
v string
}
}
and then I tried doing this:
json.NewEncoder(w).Encode(APIDoc.ResolutionValue{"foo"})
but it says that
APIDoc.ResolutionValue undefined (type APIDoc has no method
ResolutionValue)
so I resorted to doing this:
type ResolutionValue struct {
v string
}
type APIDoc struct {
Route string
ResolutionValue ResolutionValue
}
and then doing:
json.NewEncoder(w).Encode(ResolutionValue{"foo"})
kinda lame tho, is there a way to ensure integrity somehow?
As of Go 1.11, nested types are not supported.
Proposal discussion
Your revision looks a lot nicer IMO.
Edit: Maybe unrelated to the question but you can use type embedding to simplify your types. However, note that the representation are different:
type Inner struct {
Whatever int
}
type ResolutionValue struct {
Val string
Inner
}
type ResolutionValue2 struct {
Val string
Inner Inner
}
func main() {
a, _ := json.Marshal(ResolutionValue{})
b, _ := json.Marshal(ResolutionValue2{})
fmt.Printf("%s\n%s", a, b)
}
Which prints:
{"Val":"","Whatever":0}
{"Val":"","Inner":{"Whatever":0}}
Foreign application API gives me a list of names in JSON format. I need modify all of those.
But I do not like to write some loop for it (especially after Python using with reflection and stuff)
Is there any method to write something like this in Go?
type MyIncredibleType struct {
Name ModifyName // ModifyName is not a type!
}
func ModifyName(input string) string {
return input + ".com"
}
The expected behavior of this is:
a := MyIncredibleType{Name: "Abracadabra"}
print(a.Name) // Abracadabra.com
This seems pretty straight forward to me, assuming I understand your question correctly:
// ModifyName func
func ModifyName(input string) string {
return fmt.Sprintf("%v.com", input)
}
If you wish to achieve this within the type itself, without modifying (mutating) the internal state:
type MyType sturct {
name string // unexported
}
// accessor func to return name
func (t MyType) Name() string {
return t.name
}
// accessor func to return modified name
func (t MyType) ModifiedName() string {
return fmt.Sprintf("%v.com", t.name)
}
If you want to modify the internal state:
type MyType struct {
name string
}
// mutator func (note the pointer for pass by reference)
func (t *MyType) ModifyName(input string) {
t.name = fmt.Sprintf("%v.com", input)
}
// accessor (note no pointer for pass by value)
func (t MyType) Name() string {
return t.name
}
This is is not possible in GO. That's not how struct works in Go.
type MyIncredibleType struct {
Name ModifyName `json:"name"` // ModifyName is not a type!
}
you can only define Built-in types for your fields of struct or you can define Composite Literal types.
Composite literals construct values for structs, arrays, slices, and
maps and create a new value each time they are evaluated. They consist
of the type of the literal followed by a brace-bound list of elements.
Each element may optionally be preceded by a corresponding key.
Try to create a method receiver of struct which you are using to parse json coming from the api to modify the name. That will let you achieve something similar to what you want.
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func(myIncredibleType *MyIncredibleType) ModifyName() string {
return myIncredibleType.Name+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := a.ModifyName()
fmt.Printf("%s",name)
}
Playground Example
Or you can pass an interface which will wrap any struct value with name field and then use Type assertion to get the underlying value to modify the same and return the result:
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func ModifyName(input interface{}) string{
return input.(interface{}).(string)+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := ModifyName(a.Name)
fmt.Printf("%s",name)
}
Working code on Go Playground
For more information also go through Golang method Declarations on how to create receivers.
I have the following structs...
type Menu struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
Mixers []*Mixer `protobuf:"bytes,4,rep,name=mixers" json:"mixers,omitempty"`
Sections []*Section `protobuf:"bytes,5,rep,name=sections" json:"sections,omitempty"`
}
And...
type Menu struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Description string `json:"description" bson:"description"`
Mixers []Mixer `json:"mixers" bson:"mixers"`
Sections []Section `json:"sections" bson:"sections"`
}
I basically need to convert between the two struct types, I've attempted to use mergo, but that can only merge structs that are assignable to one another. The only solution I have so far is iterating through each struct, converting the ID by re-assigning it and converting its type between string and bson.ObjectId. Then iterating through each map field and doing the same. Which feels like an inefficient solution.
So I'm attempting to use reflection to be more generic in converting between the two ID's. But I can't figure out how I can effectively merge all of the other fields that do match automatically, so I can just worry about converting between the ID types.
Here's the code I have so far...
package main
import (
"fmt"
"reflect"
"gopkg.in/mgo.v2/bson"
)
type Sub struct {
Id bson.ObjectId
}
type PbSub struct {
Id string
}
type PbMenu struct {
Id string
Subs []PbSub
}
type Menu struct {
Id bson.ObjectId
Subs []Sub
}
func main() {
pbMenus := []*PbMenu{
&PbMenu{"1", []PbSub{PbSub{"1"}}},
&PbMenu{"2", []PbSub{PbSub{"1"}}},
&PbMenu{"3", []PbSub{PbSub{"1"}}},
}
newMenus := Serialise(pbMenus)
fmt.Println(newMenus)
}
type union struct {
PbMenu
Menu
}
func Serialise(menus []*PbMenu) []Menu {
newMenus := []Menu{}
for _, v := range menus {
m := reflect.TypeOf(*v)
fmt.Println(m)
length := m.NumField()
for i := 0; i < length; i++ {
field := reflect.TypeOf(v).Field(i)
fmt.Println(field.Type.Kind())
if field.Type.Kind() == reflect.Map {
fmt.Println("is map")
}
if field.Name == "Id" && field.Type.String() == "string" {
// Convert ID type
id := bson.ObjectId(v.Id)
var dst Menu
dst.Id = id
// Need to merge other matching struct fields
newMenus = append(newMenus, dst)
}
}
}
return newMenus
}
I'm can't just manually re-assign the fields because I'm hoping to detect maps on the structs fields and recursively perform this function on them, but the fields won't be the same on embedded structs.
Hope this makes sense!
I think that it is probably better to write your own converter, because you will always have some cases that are not covered by existing libs\tools for that.
My initial implementation of it would be something like this: basic impl of structs merger
I'm new working on golang, In this case I have a map [string] which has a struct.
At this point everything is works.
But I would like to have a map[string] in which I can access at the same time to another map[string] which has it self struct.
This is the code in which I'm trying to work.
type myStruct struct{
atrib1 string
atrib2 string
}
var apiRequest map[string] map[string]myStruct
I would like acces to something like this:
func main() {
apiRequest = make(map[string] map[string]myStruct)
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib1 = "first Value first op"
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib2 = "second Value first op"
apiKeyTypeRequest["Key"]["MysecondtOption"].atrib1 = "first Value second op"
}
An alternative to using a map inside a map, is to have a single map[mapKey] where mapKey is a struct:
type mapKey struct {
Key string
Option string
}
The benefits is that you only have to do a single lookup when searching for a myStruct, and you only have to create a single map.
The downside is in case you need to be able to get that options map[string]myStruct map, since it does not exist. Also, you cannot check if a certain key exists or not, because keys and options exists in pairs.
Working example:
package main
import "fmt"
type myStruct struct {
atrib1 string
atrib2 string
}
type mapKey struct {
Key string
Option string
}
func main() {
apiKeyTypeRequest := make(map[mapKey]myStruct)
apiKeyTypeRequest[mapKey{"Key", "MyFirstOption"}] = myStruct{"first Value first op", "second Value first op"}
apiKeyTypeRequest[mapKey{"Key", "MysecondtOption"}] = myStruct{atrib1: "first Value second op"}
fmt.Printf("%+v\n", apiKeyTypeRequest)
}
Playground: http://play.golang.org/p/tGd7ja7QI2
To expand upon previous answers, each map must be declared and instantiated (as well as the struct at the end of the map), that means you'll need to instantiate the "outer" map
mapOfMaps := make(map[string]map[string]myStruct)
as well as the "inner" map(s) for each key you have.
mapOfMaps[key] = make(map[string]myStruct)
The obvious issue you run into here is how do you dynamically check to see if the mapOfMaps[key] has already been instantiated? You do this using the following syntax:
if _, ok := mapOfMaps[key]; !ok {
mapOfMaps[key] = make(map[string]myStruct)
}
This syntax checks to see if mapOfMaps already has an instantiated value for that key, and if not instantiates it. Similarly, you can use the same syntax to instantiate the structs you are using by checking to see of mapOfMaps[key][key2] has been instantiated yet.
if _, ok := mapOfMaps[key][key2]; !ok {
mapOfMaps[key][key2] = new(myStruct)
}
Now you can access the struct through the call to 2 keys and an attribute of the struct
fmt.Println(mapOfMaps[key][key2].atrib1)
As #FUZxxl has said, you need to initialize the sub-map for each outer map, but there is a shorthand syntax for it:
type myStruct struct {
atrib1 string
atrib2 string
}
func main() {
var myMap = map[string]map[string]myStruct{
"foo": {
"bar": {attrib1: "a", attrib2: "b"},
"baz": {"c", "d"}, //or by order...
},
"bar": {
"gaz": {"e", "f"},
"faz": {"g", "h"},
},
}
fmt.Println(myMap["foo"]["bar"].atrib1)
fmt.Println(myMap["bar"]["gaz"].atrib2)
}