Skip to content

Instantly share code, notes, and snippets.

@alexkappa
Created March 9, 2020 13:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexkappa/7f868284bcef10da96a40e6ece63698d to your computer and use it in GitHub Desktop.
Save alexkappa/7f868284bcef10da96a40e6ece63698d to your computer and use it in GitHub Desktop.
Auth0 Go SDK unmarshal polymorphic structs using discriminator
package management
import (
"encoding/json"
"reflect"
)
type S struct {
// The discriminator used to undertsand which concrete type of Option we
// should use to unmarshal into. Can be either [foo, bar].
Strategy *string `json:"strategy,omitempty"`
RawOptions json.RawMessage `json:"options,omitempty"`
*FooOptions `json:"-"`
*BarOptions `json:"-"`
}
func (s *S) MarshalJSON() ([]byte, error) {
type aS S
v := reflect.ValueOf(s).Elem()
for _, name := range []string{
"FooOptions",
"BarOptions",
} {
f := v.FieldByName(name)
if !f.IsNil() {
b, err := json.Marshal(f.Interface())
if err != nil {
return nil, err
}
s.RawOptions = b
}
}
return json.Marshal((*aS)(s))
}
func (s *S) UnmarshalJSON(b []byte) error {
type aS S
err := json.Unmarshal(b, (*aS)(s))
if err != nil {
return err
}
if s.Strategy != nil {
var v interface{}
switch *s.Strategy {
case "foo":
v = &s.FooOptions
case "bar":
v = &s.BarOptions
default:
return nil
}
err := json.Unmarshal(s.RawOptions, v)
if err != nil {
return err
}
}
return nil
}
type FooOptions struct {
ClientID *string `json:"client_id,omitempty"`
ClientSecret *string `json:"client_secret,omitempty"`
Domain *string `json:"domain,omitempty"`
TenantDomain *string `json:"tenant_domain,omitempty"`
DomainAliases []interface{} `json:"domain_aliases,omitempty"`
}
type BarOptions struct {
ClientID *string `json:"client_id,omitempty"`
ClientSecret *string `json:"client_secret,omitempty"`
Contacts *bool `json:"contacts,omitempty"`
Calendar *bool `json:"calendar,omitempty"`
GooglePlus *bool `json:"google_plus,omitempty"`
PicasaWeb *bool `json:"picasa_web,omitempty"`
Youtube *bool `json:"youtube,omitempty"`
GoogleAffiliateNetwork *bool `json:"google_affiliate_network,omitempty"`
GoogleBooks *bool `json:"google_books,omitempty"`
Email *bool `json:"email,omitempty"`
Profile *bool `json:"profile,omitempty"`
}
package management
import (
"encoding/json"
"testing"
"gopkg.in/auth0.v3"
)
func TestJSONUnmarshal(t *testing.T) {
for _, j := range []string{
`{"strategy": "foo", "options": {"domain": "example.com"}}`,
`{"strategy": "bar", "options": {"email": true, "profile": true}}`,
`{"options": {"baz": "true"}}`,
} {
var s S
err := json.Unmarshal([]byte(j), &s)
if err != nil {
t.Error(err)
}
if s.Strategy != nil {
switch *s.Strategy {
case "foo":
t.Logf("%s %s %s", *s.Strategy, *s.FooOptions.Domain, s.RawOptions)
case "bar":
t.Logf("%s %t %t %s", *s.Strategy, *s.BarOptions.Email, *s.BarOptions.Profile, s.RawOptions)
}
} else {
t.Logf("%s", s.RawOptions)
}
}
}
func TestJSONMarshal(t *testing.T) {
for _, s := range []S{
{
Strategy: auth0.String("foo"),
FooOptions: &FooOptions{
Domain: auth0.String("example.com"),
},
},
{
Strategy: auth0.String("bar"),
BarOptions: &BarOptions{
Email: auth0.Bool(true),
Profile: auth0.Bool(true),
},
},
} {
b, err := json.Marshal(&s)
if err != nil {
t.Error(err)
}
t.Logf("%s\n", b)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment