Refer to inner struct - go

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}}

Related

How do i recover the struct type after make it as Interface in Golang

I want to merge some similar func code to one func, but every old func is use different type of struct, so i intend to create the model by different string of type.
SO i do something like this:
type A struct {
filed string
}
type B struct {
filed string
}
and still C, D, E, F here...(every struct has its own method different with others)
and i want create those type in one place:
create(typeName string) interface {
switch typeName {
case A:
return &A{}
case B:
return &B{}
....(more case than 10 times)
}
}
Then i use the create() here:
model := create("A")
now, model is type of interface, and no A`s fileds, how would simply to recover the type of model to A
Here is a sample of how you can use type assertion to convert interfaces to underlying structs
Here e is of the struct type and hence you can access any of its fields or struct methods.
package main
import (
"fmt"
)
type A struct {
AF int
}
type B struct {
BF string
}
func main() {
ds := []interface{}{
A{1},
B{"foo"},
}
for _, d := range ds {
switch e := d.(type) {
case A:
fmt.Println(e.AF)
case B:
fmt.Println(e.BF)
}
}
}

How to merge 2 structs

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)
}

How to fill a struct that has lots of nested structs in Go

What is the best way to fill a struct that has lots of nested structs inside it?
I made a struct to generate a json schema file from it that looks like this:
type Schema struct {
Schema string `default:"http://json-schema.org/draft-04/schema#"`
Title string `default:"Test Schema"`
Type string `default:"object"`
AdditionalProperties bool `default:false`
Properties struct {
Core struct {
Type string
AdditionalProperties bool
Properties struct{}
}
Work struct {
Type string
AdditionalProperties bool
Properties struct{}
}
}
}
At first, I wanted to put the default data in tags and fill the struct from that, but reflect package doesn't look inside the nested structs.
Here is what I did using reflect:
t := reflect.TypeOf(Schema{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag.Get("default")
}
The best way to do this is with a constructor method. This will be more readable, and much faster, than using tags plus reflection. Something like:
func NewSchema() *Schema {
return &Schema{
Schema: "http://json-schema.org/draft-04/schema#",
...
}
}

map[string]string inside a struct

Apologies for the title, but this is a weird one and out of my ability to understand.
I'm working with a go library that's sort of finished, but sort of not:
https://github.com/yfronto/go-statuspage-api
The statuspage.io API supports the following params when posting an incident:
incident[components][component_id] - Map of status changes to apply to affected components.
An example would be:
"incident[components][ftgks51sfs2d]=degraded_performance"
Unfortunately, the struct defined in the library doesn't support that particular field:
type NewIncidentUpdate struct {
Name string
Status string
Message string
WantsTwitterUpdate bool
ImpactOverride string
ComponentIDs []string
}
func (i *NewIncidentUpdate) String() string {
return encodeParams(map[string]interface{}{
"incident[name]": i.Name,
"incident[status]": i.Status,
"incident[message]": i.Message,
"incident[wants_twitter_update]": i.WantsTwitterUpdate,
"incident[impact_override]": i.ImpactOverride,
"incident[component_ids]": i.ComponentIDs,
})
}
How can I update this struct (and the associated encodeParams function) to support passing an arbitrary map of components and associated statuses?
You could embed a NewIncidentUpdate inside your own struct that defines the component status changes.
type MyIncidentUpdate struct {
NewIncidentUpdate
ComponentStatusChanges map[string]string
}
then define String the same way, while accomodating for your ComponentStatusChanges map.
func (i *MyIncidentUpdate) String() string {
params := map[string]interface{}{
"incident[name]": i.Name,
"incident[status]": i.Status,
"incident[message]": i.Message,
"incident[wants_twitter_update]": i.WantsTwitterUpdate,
"incident[impact_override]": i.ImpactOverride,
"incident[component_ids]": i.ComponentIDs,
}
for k, val := range i.ComponentStatusChanges {
key := fmt.Sprintf("incident[components][%s]", k)
params[key] = val
}
return encodeParams(params)
}

Conditional variable declaration in golang?

Is it possible to do conditional variable type declaration like this in Golang?
if isAdmin {
var result NormalResult
} else {
var result AdminResult
}
// do something to &result
doSomething(&result)
func doSomething(interface{}) {
// something
}
The above does not work, but the ideas is that normalResult and adminResults are very similar structs and how would I go about doing this?
Thank you!
No, not in this manner. Go being statically typed, needs to know the type information at compile time.
What you could do is declare result as an interface of some type which both AdminResult and NormalResult satisfy. You can then use a type assertion at runtime to decide which type of result it is.
(You also have to declare result outside of the if blocks because Go is block scoped)
type NormalResult struct {
Value int
}
func (r NormalResult) Result() int {
return r.Value
}
type AdminResult struct {
Value int
}
func (r AdminResult) Result() int {
return r.Value
}
type Resulter interface {
Result() int
}
func main() {
isAdmin := true
var r Resulter
if isAdmin {
r = AdminResult{2}
} else {
r = NormalResult{1}
}
fmt.Println("Hello, playground", r)
}
Depending on what kind of similarities, you might have different options.
Using embedded structs
Depending on your structure, you might be able to use embedded structs. Let's say NormalResult is defined like this:
type NormalResult struct {
Name string
Value int
}
And if AdminResult shares the same properties but just adds a few more of them (like UserId), you can choose to embed NormalResult into the AdminResult like this:
type AdminResult struct {
*NormalResult
UserId int64
}
Then you can also declare methods for NormalResult which will be promoted to AdminResult as well:
func (r *NormalResult) doSomething() {
// Doing something
}
Edit
And, no, it is not possible to have conditional types in Go as you suggested. A variable can only be of one type, be it NormalResult, AdminResult or interface{}

Resources