Open fengye87 opened 10 months ago
This should still work if you define Child as a poly value, the same as on the top level of the example in the readme.
Thanks for pointing it out. I managed to make it work. (BTW: The sample code in the README.md is flawed.) The code I'm using:
package main
import (
"encoding/json"
"fmt"
"github.com/flachnetz/poly"
"github.com/samber/lo"
)
func main() {
bytes := lo.Must(json.Marshal(PolySpeark{Animal{
Speaker: PolySpeark{Dog{}},
}}))
fmt.Println(string(bytes))
var speaker PolySpeark
lo.Must0(json.Unmarshal(bytes, &speaker))
speaker.Value.Speak()
}
type Speaker interface{ Speak() }
type Dog struct{ IsDog bool }
func (Dog) Speak() {
fmt.Println("wuff")
}
type Cat struct{ IsCat bool }
func (Cat) Speak() {
fmt.Println("miau")
}
type Animal struct {
Speaker PolySpeark
}
func (c Animal) Speak() {
c.Speaker.Value.Speak()
}
type PolySpeark poly.Poly[Speaker, poly.TypeItem[Animal, poly.TypeItem[Dog, poly.TypeItem[Cat, poly.Nil]]]]
func (p PolySpeark) MarshalJSON() ([]byte, error) {
return json.Marshal((*poly.Poly[Speaker, poly.TypeItem[Animal, poly.TypeItem[Dog, poly.TypeItem[Cat, poly.Nil]]]])(&p))
}
func (p *PolySpeark) UnmarshalJSON(bytes []byte) error {
return json.Unmarshal(bytes, (*poly.Poly[Speaker, poly.TypeItem[Animal, poly.TypeItem[Dog, poly.TypeItem[Cat, poly.Nil]]]])(p))
}
Now the problem becomes: I have dozens of implementations of Speaker
, and those last several lines would become a nightmare. Any thoughts?
It works by using a type alias and pointers. I'll need to have another look on why exactly this is the case. I've probably only implemented json.Marshaller for *Poly
. I'll have a look later:
Your example with the necessary changes:
package main
import (
"encoding/json"
"fmt"
"github.com/flachnetz/poly"
"github.com/samber/lo"
)
func main() {
bytes := lo.Must(json.Marshal(
&PolySpeark{
Animal{
Speaker: &PolySpeark{Dog{}},
},
},
),
)
fmt.Println(string(bytes))
var speaker PolySpeark
lo.Must0(json.Unmarshal(bytes, &speaker))
speaker.Value.Speak()
}
type Speaker interface{ Speak() }
type Dog struct{ IsDog bool }
func (Dog) Speak() {
fmt.Println("wuff")
}
type Cat struct{ IsCat bool }
func (Cat) Speak() {
fmt.Println("miau")
}
type Animal struct {
Speaker *PolySpeark
}
func (c Animal) Speak() {
c.Speaker.Value.Speak()
}
type PolySpeark = poly.Poly[Speaker, poly.TypeItem[Animal, poly.TypeItem[Dog, poly.TypeItem[Cat, poly.Nil]]]]
Type alias works in the example, but not in my real case. I have multiple implementations that is recursive. Try include a Speaker *PolySpeark
field in Dog
, the go compiler would complain about "invalid use of type alias" then.
I have a rough thought here. How about pushing reflection further and also taking in implementation types registration? Something like:
type Final struct {
...
Poly Iface
}
var ifacePoly = poly.New[Iface]().Impls(ImplA{}, ImplB{})
func (f Final) MarshalJSON(...) ... {
return ifacePoly.MarshalJSON(f)
}
type Iface interface {
...
}
type ImplA struct {
...
}
type ImplB struct {
....
Poly Iface
}
The benefits are:
Final
whose type is Iface
would get marshaled as expected magically.
I bumped into this project while looking for a lib to solve marshaling my poly struct to JSON (and back from it too). But my poly struct is recursive. Say like
So
Child
would not marshal/unmarshal as expected here. Any way to support it?