In my understanding, there is no inherent and polymorphism in golang, so how to get the sub-struct from a given struct? For example, struct A has several attributes:
type A struct {
a int
b string
c *float64
d []byte
e map[string]interface{}
}
I want to delete the e:
type B struct {
a int
b string
c *float64
d []byte
}
So is there any way to convert A to B except copy variables one by one?
You can do that with embedded fields. See Struct Types in the Go Language Specification, e.g.:
Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField).
With an embedded field, you can copy the fields in one struct to the struct in which it is an embedded field with a simple assignment to the field.
Here's a complete, runnable example:
package main
import (
"encoding/json"
"fmt"
)
type Shared struct {
Id int
Name string
}
type A struct {
Count int
Shared
}
type B struct {
Color string
Shared
}
func main() {
shared := Shared{
Id: 1,
Name: "john",
}
a := A{
Count: 5,
Shared: shared,
}
b := B{
Color: "green",
}
b.Shared = a.Shared
c := map[string]interface{}{
`a`: a,
`b`: b,
}
msg, err := json.Marshal(c)
if err != nil {
fmt.Printf("Marshal error: %s\n", err)
}
fmt.Println(string(msg))
}
Prints:
{"a":{"Count":5,"Id":1,"Name":"john"},"b":{"Color":"green","Id":1,"Name":"john"}}
Related
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)
}
}
}
Is there any way to create a map into several structs, and then use It?
I have several different structs that implement the same interface and matching input types for each struct.
I want to read data from the different inputs into the structs - without knowing the input type at the compilation time.
type myInput struct {
InputType string
data []bytes
}
// Will get as an input after compeleation
inputs := []myInput{
myInput{InputType: "a", data: []bytes{0x01, 0x02, 0x03}},
myInput{InputType: "b", data: []bytes{0x01, 0x02}},
}
type StructA struct {
A uint16
B uint32
}
func (a StructA) name () {
fmt.Printf("name is: %d %d", a.A, a.B)
}
type StructB struct {
C uint32
}
func (b StructB) name () {
fmt.Printf("name is: %d", b.C)
}
AorB map[string]<???> {
"a": StructA,
"b": StructB,
}
At this point, I don't know what to do. I need to take the correct struct by the input type and initialize the struct using binary.Read.
for _, myInput := range (inputs) {
// ???? :(
myStruct := AtoB[myInput.InputType]{}
reader :=bytes.NewReader(input1)
err := binary.Read(reader, binary.BigEndian, &myStruct)
fmt.Printf(myStruct.name())
}
Thanks!
Define a interface
type Bin interface {
name() string
set([]byte) // maybe returning error
}
You'll be handling Bins only.
type StructA struct { /* your code so far */ }
type StructB struct { /* your code so far */ }
func (a *StructA) set(b []byte) {
a.A = b[0]<<8 + b[1] // get that right, too lazy to code this for you
a.B = b[2]<<24 + b[3]<<16 + ...
}
// same for StructB
So your StructA/B are Bins now.
func makeBin(in myInput) Bin {
var bin Bin
if in.InputType == "a" {
bin = &StructA{}
} else {
bin = &StructB{}
}
bin.set(in.data) // error handling?
return bin
}
If you have more than two types: Use a switch instead if an if or make a tiny type registry (reflect).
First you define an interface for the commonly used func name:
type Namer interface {
name()
}
Then you can create a map to that interface and insert structs:
AorB := map[string] Namer {
"a": StructA{
A: 42,
B: 28,
},
"b": StructB{
C: 12,
},
}
Now you can access all entries:
for _, n := range AorB {
n.name()
}
You can use interface for the same
AorB := map[string]interface{}{
"a": StructA{},
"b": StructB{},
}
When you retrieve value back you can assert into A for type A and B for type B
I have a struct that looks like this:
type Type int
const (
A Type = iota
B
C
)
type Character struct {
Type Type `json:"type"`
}
When I call json.Marshal(...) on the struct, is there a way that the json:"type" representation is a string called either "A", "B", or "C"?
When this is presented in JSON, nobody is going to know what 0, 1, or 2 is, so the name of the constant is more useful.
Apologies if this has been asked before. I googled all over and couldn't find anything.
Here is an example:
type Type int
const (
A Type = iota
B
C
)
type Character struct {
Type Type `json:"type,string"`
}
func main() {
c := Character{}
c.Type = A
j, err := json.Marshal(c)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
I want fmt.Println(string(j)) to print {"type":"A"}, not {"type":0}.
You need to define a custom marshaller for your type.
type Type int
const (
A Type = iota
B
C
)
var typeToString = map[Type]string{
A: "A",
B: "B",
C: "C",
}
func (t Type) MarshalJSON() ([]byte, error) {
return json.Marshal(typeToString[t])
}
type Character struct {
Type Type `json:"type"`
}
func main() {
c := Character{}
c.Type = A
j, err := json.Marshal(c)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
The function MarshalJSON defines how json.Marshal should marshal your type. You can do something similar for unmarshaling if you need to go the other direction as well.
See https://play.golang.org/p/mLxThWA19by.
Short reply: there's NO direct option to achieve your goal.
Long reply: truth is, you can actually override the way Go can encode structs.
Here's the working code:
https://play.golang.org/p/i92pUpNG-Wr
package main
import (
"encoding/json"
"fmt"
)
// please consider to rename this!
type Type int
const (
A Type = iota
B
C
)
type Character struct {
Type Type `json:"-"`
}
func (c *Character) mapTypeToString() string {
switch c.Type {
case B:
return "B"
case C:
return "C"
}
// defaults on A
return "A"
}
// MarshalJSON overwrites the standard JSON marshal.
func (c *Character) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Type string `json:"type"`
}{
Type: c.mapTypeToString(),
})
}
func main() {
c := &Character{}
c.Type = A
j, err := json.Marshal(c)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
Be aware of the c := &Character{} initialization.
The tradeoff would be to have the mapTypeToString() method that needs to be updated according to the different types of ... Type.
Please, also consider to avoid naming structs and vars like Type :)
So your JSON API gives you the type A:
I want fmt.Println(string(j)) to print {"type":"A"}, not {"type":0}.
You could change your code like this, then your API works:
https://play.golang.org/p/ypvFvQpBw-C
type JSONType string
const (
A JSONType = "A"
B JSONType = "B"
C JSONType = "C"
)
type Character struct {
JSONType JSONType `json:"type,string"`
}
func main() {
c := Character{}
c.JSONType = A
j, err := json.Marshal(c)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
I'm trying to convert an interface dynamically back to it's original struct but I am having issues accessing attributes of the struct after the conversion.
Take this code for example.
package main
import (
"fmt"
"log"
)
type struct1 struct {
A string
B string
}
type struct2 struct {
A string
C string
}
type struct3 struct {
A string
D string
}
func main() {
s1 := struct1{}
s1.A = "A"
structTest(s1)
s2 := struct2{}
s2.A = "A"
structTest(s2)
s3 := struct3{}
s3.A = "A"
structTest(s3)
}
func structTest(val interface{}) {
var typedVal interface{}
switch v := val.(type) {
case struct1:
fmt.Println("val is struct1")
case struct2:
fmt.Println("val is struct2")
case struct3:
fmt.Println("val is struct3")
default:
log.Panic("not sure what val is.")
}
fmt.Println(typedVal.A)
}
I would like to be able to pass in one of 3 known struct types into my function. Then figure out which struct type was passed in to type assert it. Finally I want to be able to access like attributes.
Basically I want to have some basic inheritance in my structs, but so far it seems that it is not possible to do this in go. I saw some posts mentioning inheritance using an interface, but my structs have no methods so I'm not sure how I would use an interface.
Is something like this possible in go?
I would like to be able to pass in one of 3 known struct types into my function. Then figure out which struct type was passed in to type assert it. Finally I want to be able to access like attributes.
You can use type assertions to do exactly that. Basic idea is, in any case of the type switch just use type assertion to get a concrete instance of the corresponding type and then you can call whatever properties that you wish.
Take a look at the following example
package main
import (
"fmt"
)
type test1 struct {
A, B string
}
type test2 struct {
A, C string
}
func testType(val interface{}) {
switch val.(type) {
case test1:
t := val.(test1)
fmt.Println(t.B)
break
case test2:
t := val.(test2)
fmt.Println(t.C)
break
}
}
func main() {
t1, t2 := test1{B: "hello"}, test2{C: "world"}
testType(t1)
testType(t2)
}
Playground
Function structTest(val interface{}) in your code seems to be loosely typed. You pass it an untyped argument and expect it will satisfy some condition (will have field A), it looks strange in any typed language.
Using an interface this kind of polymorphism, in Go, to my mind, can be expressed something like
package main
import (
"fmt"
"log"
)
type A string
type HasA interface {
PrintA()
}
func (a A) PrintA() { fmt.Println(a) }
type struct1 struct {
A
B string
}
type struct2 struct {
A
C string
}
type struct3 struct {
A
D string
}
func main() {
s1 := struct1{}
s1.A = "A"
structTest(s1)
s2 := struct2{}
s2.A = "A"
structTest(s2)
s3 := struct3{}
s3.A = "A"
structTest(s3)
}
func structTest(val HasA) {
switch val.(type) {
case struct1:
fmt.Println("val is struct1")
case struct2:
fmt.Println("val is struct2")
case struct3:
fmt.Println("val is struct3")
default:
log.Panic("not sure what val is.")
}
val.PrintA()
}
Playground
Code below generates this error json: cannot unmarshal number into Go struct field TMP.a of type string
package main
import (
"encoding/json"
"fmt"
)
var b = []byte(`{"a": "str", "A": 123}`)
type TMP struct {
// A interface{} `json:"a"`
A string `json:"a"`
// A int `json:"A"`
}
func main() {
var tmp TMP
err := json.Unmarshal(b, &tmp)
if err != nil {
fmt.Println(err)
}
}
I've read through https://golang.org/pkg/encoding/json/#Marshal and cannot find anything that states this shouldn't work. What am I missing?
It's been asked that I clarify, and rightly so. What I'm really wondering is why when I use JSON with only 2 keys that differ in uppercase/lowercase why Unmarshal is not keeping the case I've provided in the struct json tag.
First of all change the names of your fields. Since they have same name only uppercase A and lowercase a. So when go try to marshal the fields it is unable to recognize between the fields.
package main
import (
"encoding/json"
"fmt"
)
var b = []byte(`{"a": "str", "B": 123}`)
type TMP struct {
// A interface{} `json:"a"`
A string `json:"A"`
B int `json:"B"`
}
func main() {
var tmp TMP
err := json.Unmarshal(b, &tmp)
if err != nil {
fmt.Println(err)
}
fmt.Println(tmp)
}
As the error says
json: cannot unmarshal string into Go struct field TMP.A of type int
even if you try to pass both fields like below it will give same error
var b = []byte(`{"a": "str", "A": 123}`)
type TMP struct {
// A interface{} `json:"a"`
a string `json:"a"`
A int `json:"A"`
}
The real problem is that you're trying to unmarshal int to a string field. Error will occur even if you remove the "a": "str", and will work fine if you double quote the 123
What is a little funny, however, is that the case is ignored with only one field. Changing your struct to:
type TMP struct {
// A interface{}json:"a"
A stringjson:"a"
B intjson:"A"
}
also works.