Getting error while access the struct type of array element as undefined (type []ParentIDInfo has no field or method PCOrderID) - go

I am new to golang and I have one issue which I think community can help me to solve it.
I have one data structure like below
type ParentIDInfo struct {
PCOrderID string `json:"PCorderId",omitempty"`
TableVarieties TableVarietyDC `json:"tableVariety",omitempty"`
ProduceID string `json:"PRID",omitempty"`
}
type PCDCOrderAsset struct {
PcID string `json:"PCID",omitempty"`
DcID string `json:"DCID",omitempty"`
RequiredDate string `json:"requiredDate",omitempty"`
Qty uint64 `json:"QTY",omitempty"`
OrderID string `json:"ORDERID",omitempty"`
Status string `json:"STATUS",omitempty"`
Produce string `json:"Produce",omitempty"`
Variety string `json:"VARIETY",omitempty"`
Transports []TransportaionPCDC `json:"Transportaion",omitempty"`
ParentInfo []ParentIDInfo `json:"ParentInfo",omitempty"`
So I have issue to access the PCOrderID which inside the []ParentIDInfo . I have tried below however I getting error as "pcdcorder.ParentInfo.PCOrderID undefined (type []ParentIDInfo has no field or method PCOrderID)"
keyfarmercas = append(keyfarmercas, pcdcorder.ParentInfo.PCOrderID)
Any help will be very good
Thanks in advance

PCDCOrderAsset.ParentInfo is not a struct, it does not have a PCOrderID field. It's a slice (of element type ParentIDInfo), so its elements do, e.g. pcdcorder.ParentInfo[0].PCOrderID.
Whether this is what you want we can't tell. pcdcorder.ParentInfo[0].PCOrderID gives you the PCOrderID field of the first element of the slice. Based on your question this may or may not be what you want. You may want to append all IDs (one from each element). Also note that if the slice is empty (its length is 0), then pcdcorder.ParentInfo[0] would result in a runtime panic. You could avoid that by first checking its length and only index it if its not empty.
In case you'd want to add ids of all elements, you could use a for loop to do that, e.g.:
for i := range pcdorder.ParentInfo {
keyfarmercas = append(keyfarmercas, pcdcorder.ParentInfo[i].PCOrderID)
}

Related

How do you access this Go struct? [duplicate]

This question already has an answer here:
Strange type definition syntax in Golang (name, then type, then string literal)
(1 answer)
Closed 1 year ago.
I'm trying to make sense of this Go struct:
type ListClustersOutput struct {
_ struct{} `type:"structure"`
// A list of all of the clusters for your account in the specified Region.
Clusters []*string `locationName:"clusters" type:"list"`
// The nextToken value to include in a future ListClusters request. When the
// results of a ListClusters request exceed maxResults, you can use this value
// to retrieve the next page of results. This value is null when there are no
// more results to return.
NextToken *string `locationName:"nextToken" type:"string"`
}
Looking at the docs: https://golangdocs.com/structs-in-golang#defining-a-struct-in-go
it gives an example:
type Fruit struct {
name string
}
which seems very different.
In the more complex code, I assume this Clusters []*string `locationName:"clusters" type:"list"` is equivalent to name string but struggling to unpack it.
I'm struggling to find much out about type: "list" - most of the examples seem to refer to slices. Why are they using a list?
what is a locationName?
how do you access the first element of the list in that struct?
Note, for this last question, if I use result.Clusters[0] (where result is of this struct type) I get a pointer. E.g.
fmt.Println("Result: ", result.Clusters[0])
Result: 0xc000372260
How do I dereference it?
Looking at this:
How does pointer dereferencing work in Go?
it seems you need an asterisk or an ampersand. Not clear which one you use or whether you tack it on the beginning or the end.
You are struggling with struct tags.
In your code:
Clusters []*string `locationName:"clusters" type:"list"`
The Clusters field has a type ([]*string) and the rest of the declaration are 2 struct tags that you should take the value(s) of tags using struct tag.
Here's how you access it:
fmt.Println("Result: ", *result.Clusters[0])

How to insert a struct with nil field to mongo?

I am trying to insert a struct to mongo. Firstly I get the data from an API as JSON and assign the data to a struct. Some fields might be nil. After that I insert the struct to mongoDB. So the problem I get is that when inserted, all the fields are initialized. For example I have a struct like this:
type VirtualMachine struct {
VirtualMachineID utils.SUUID `bson:"VirtualMachineID"`
Cdroms []*VM.VirtualMachineCdrom `bson:"Cdroms"`
CpuAllocatedMHz int `bson:"CpuAllocatedMHz"`
Name string `bson:"Name"`
}
If I get Json data like this
{
"VirtualMachineID":'16as4df663a',
"Cdroms":null,
"CpuAllocatedMHz":1666,
"Name":'VMName'
}
after I put it to mongo, the null field becomes an empty array. I need to avoid that. 'omitempty' did not help because it skips the field as well if the provided field happens to be an empty array and not null.
Firstly I thought it was because of the pointers, but later I found that the same happens to all data types. Shortly, if its nil, mgo converts it to its zero value.
I think I am missing something here, because it would be weird if mgo converts all nil values to their zero values by design.
Try *[]*VM.VirtualMachineCdrom (or just *[]VM.VirtualMachineCdrom if you don't actually need the elements to be pointers). A nil slice == a zero length slice, but a nil pointer to a slice does not.

Why do I get a "cannot assign" error when setting value to a struct as a value in a map? [duplicate]

This question already has answers here:
Accessing struct fields inside a map value (without copying)
(2 answers)
Closed 7 years ago.
New to Go. Encountered this error and have had no luck finding the cause or the rationale for it:
If I create a struct, I can obviously assign and re-assign the values no problem:
type Person struct {
name string
age int
}
func main() {
x := Person{"Andy Capp", 98}
x.age = 99
fmt.Printf("age: %d\n", x.age)
}
but if the struct is one value in a map:
type Person struct {
name string
age int
}
type People map[string]Person
func main() {
p := make(People)
p["HM"] = Person{"Hank McNamara", 39}
p["HM"].age = p["HM"].age + 1
fmt.Printf("age: %d\n", p["HM"].age)
}
I get cannot assign to p["HM"].age. That's it, no other info. http://play.golang.org/p/VRlSItd4eP
I found a way around this - creating an incrementAge func on Person, which can be called and the result assigned to the map key, eg p["HM"] = p["HM"].incrementAge().
But, my question is, what is the reason for this "cannot assign" error, and why shouldn't I be allowed to assign the struct value directly?
p["HM"] isn't quite a regular addressable value: hashmaps can grow at runtime, and then their values get moved around in memory, and the old locations become outdated. If values in maps were treated as regular addressable values, those internals of the map implementation would get exposed.
So, instead, p["HM"] is a slightly different thing called a "map index expression" in the spec; if you search the spec for the phrase "index expression" you'll see you can do certain things with them, like read them, assign to them, and use them in increment/decrement expressions (for numeric types). But you can't do everything. They could have chosen to implement more special cases than they did, but I'm guessing they didn't just to keep things simple.
Your approach seems good here--you change it to a regular assignment, one of the specifically-allowed operations. Another approach (maybe good for larger structs you want to avoid copying around?) is to make the map value a regular old pointer that you can modify the underlying object through:
package main
import "fmt"
type Person struct {
name string
age int
}
type People map[string]*Person
func main() {
p := make(People)
p["HM"] = &Person{"Hank McNamara", 39}
p["HM"].age += 1
fmt.Printf("age: %d\n", p["HM"].age)
}
The left side of the assignment must b "addressable".
https://golang.org/ref/spec#Assignments
Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier.
and https://golang.org/ref/spec#Address_operators
The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array.
as #twotwotwo's comment, p["HM"] is not addressable.
but, there is no such definition show what is "addressable struct operand" in the spec. I think they should add some description for it.

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

Properly distinguish between not set (nil) and blank/empty value

Whats the correct way in go to distinguish between when a value in a struct was never set, or is just empty, for example, given the following:
type Organisation struct {
Category string
Code string
Name string
}
I need to know (for example) if the category was never set, or was saved as blank by the user, should I be doing this:
type Organisation struct {
Category *string
Code *string
Name *string
}
I also need to ensure I correctly persist either null or an empty string to the database
I'm still learning GO so it is entirely possible my question needs more info.
The zero value for a string is an empty string, and you can't distinguish between the two.
If you are using the database/sql package, and need to distinguish between NULL and empty strings, consider using the sql.NullString type. It is a simple struct that keeps track of the NULL state:
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
You can scan into this type and use it as a query parameter, and the package will handle the NULL state for you.
Google's protocol buffers (https://code.google.com/p/goprotobuf/) use pointers to describe optional fields.
The generated objects provide GetFoo methods which take the pain away from testing for nil (a.GetFoo() returns an empty string if a.Foo is nil, otherwise it returns *a.Foo).
It introduces a nuisance when you want to write literal structs (in tests, for example), because &"something" is not valid syntax to generate a pointer to a string, so you need a helper function (see, for example, the source code of the protocol buffer library for proto.String).
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
return &v
}
Overall, using pointers to represent optional fields is not without drawbacks, but it's certainly a viable design choice.
The standard database/sql package provides a NullString struct (members are just String string and Valid bool). To take care of some of the repetitive work of persistence, you could look at an object-relational manager like gorp.
I looked into whether there was some way to distinguish two kinds of empty string just out of curiosity, and couldn't find one. With []bytes, []byte{} == []byte(nil) currently returns false, but I'm not sure if the spec guarantees that to always remain true. In any case, it seems like the most practical thing to do is to go with the flow and use NullString.

Resources