"invalid recursive type" and "illegal cycle in declaration of" [duplicate] - go

This question already has answers here:
invalid recursive type in a struct in go
(2 answers)
Closed 11 months ago.
Shortened example of the two custom types below. "Question" contains a "SavedQuestion" and "SavedQuestion" contains a "Question".
Errors:
1. illegal cycle in declaration of SavedQuestion
2. invalid recursive type Question
type Question struct {
ID int `json:"id"`
Name string `json:"name"`
QueryText string `json:"query_text"`
SavedQuestion SavedQuestion `json:"saved_question"`
}
type SavedQuestion struct {
ID int `json:"id"`
Index int `json:"index"`
Name string `json:"name"`
Packages PackageSpecList `json:"packages"`
QueryText string `json:"query_text"`
Question Question `json:"question"`
}
I'm modeling some types to consume a 3rd party API... The 3rd party API's objects are modeled this way. It seems perfectly normal that a "SavedQuestion" is a separate type that essentially contains a "Question" along with a bunch of other data about it... I can see the loop with a "Question" also containing a "SavedQuestion"... but that's what the API does. As you can see from the json tags I'm just trying to model exactly what the API is sending so I can marshal/unmarshal etc..
How do I make this work? I can make the "Question" in the "SavedQuestion" a pointer to a "Question" and the errors go away... however I don't know if I should or need to do that, or if it will actually work as the code is not yet complete enough to run a test...
New gopher confused...
Thanks for any input.

You cannot recursively include one struct within another for the simple reason that the size and memory layout of the struct becomes recursive. Size of Question is some data + size of (SavedQuestion), and size of SavedQuestion is size of some data + size of Question...
However, if you use pointers, the sizing problem is solved:
type Question struct {
ID int `json:"id"`
Name string `json:"name"`
QueryText string `json:"query_text"`
SavedQuestion *SavedQuestion `json:"saved_question"`
}
You need to make sure when to reference question.SavedQuestion, it is not nil. This will work just fine for marshaling/unmarshaling. When you unmarshal, if the JSON does not have a saved_question element, it'll be nil.

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

One struct multiple json representation [duplicate]

This question already has answers here:
Is it possible to have a struct with multiple JSON tags?
(2 answers)
Closed 3 years ago.
The Problem I am trying to solve is that I have a representation as below:
type Request struct{
ItId string `form:"itId"`
tR string `form:"treason"`
cd string `form:"cdetails"`
}
but I want the above one in such a manner that it can accept:
itId and ItrId both for ItId
It will be something like this:
type Request struct{
ItId string `form:"itId"` || ItId string `form:"ItrId"`
tR string `form:"treason"`
cd string `form:"cdetails"`
}
what can be the possible solution for such a case?
I am not sure which package are you importing that uses form struct tag, but to try and answer the question, I am assuming it is similar to json tag that is imported by encoding/json package.
In short, no it is not possible to have a same struct tag for a field, as answered in this SOF question - https://stackoverflow.com/a/37118633/5353730
The OP in above question was trying to accomplish something like:
type Foo struct {
Name string `json:"name" json:"employee_name"`
Age int `json:"age" json:"-"`
}
...which I assume is what you want to achieve.
If, however, you might've been trying to use different meta tags for the same field, then that is easily supportable as shown in this answer - https://stackoverflow.com/a/18635910/5353730
type Page struct {
PageId string `bson:"pageId" json:"pageId"`
Meta map[string]interface{} `bson:"meta" json:"meta"`
}
P.S. You can always create 2 separate fields, one each for itId and ItrId to solve your problem but then you will need to check for both these fields at the place of usage of struct.

What is the advantage of using a pointer to a string instead of a string in Go [duplicate]

This question already has answers here:
What does the asterisk do in "Go"?
(6 answers)
Closed 5 years ago.
Reviewing some go code I came across this:
Person struct {
Name *string `json:"name"`
}
and then some where I saw:
Animal struct {
Name string `json:"name"`
}
What is the advantage of the pointer here?
The * declares a pointer type. A pointer to a string is sometimes used when decoding JSON to distinguish the following JSON:
JSON value of the Name field
{ } nil
{name: ""} pointer to ""
Without the pointer, it's not possible to distinguish a missing value from a blank value in the decoded result.
If the application does not need to make this distinction, then use the second form shown in the question. It's more convenient.
* means pointer.
In your case, Name is a field of type pointer to string.
See http://www.golang-book.com/books/intro/8
The * is a pointer.
A pointer type denotes the set of all pointers to variables of a given
type, called the base type of the pointer. The value of an
uninitialized pointer is nil.
This is coming from the Go Spec. I would suggest reading it all.

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,

Strange type definition syntax in Golang (name, then type, then string literal)

I've been trying to find out how to use mgo (MongoDB driver for Go) and I came across this struct declaration:
type Something struct {
Id bson.ObjectId "_id,omitempty"
Name string
}
I don't quite understand the syntax of the first element (Id). I understand that it's being declared as type bson.ObjectId, but what is the string literal doing there?
My question is not about the mgo driver functionality,
but about this strange <name> <type> <string_literal> syntax.
I couldn't find anything on the Go specs, and I don't know how to google this either.
It's explained in the Struct types section of the language specification:
A field declaration may be followed by an optional string literal
tag, which becomes an attribute for all the fields in the corresponding field declaration. The tags are made visible through a
reflection interface but are otherwise ignored.
// A struct corresponding to the TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers.
struct {
microsec uint64 "field 1"
serverIP6 uint64 "field 2"
process string "field 3"
}

Resources