package main import ( "encoding/json" "errors" "fmt" "strings" ) type QCryptoBlockOptionsQCow struct { KeySecret *string `json:"key-secret,omitempty"` } type QCryptoBlockOptionsLUKS struct { KeySecret *string `json:"key-secret,omitempty"` } type QCryptoBlockOpenOptions struct { // Variants fields Qcow *QCryptoBlockOptionsQCow `json:"-"` Luks *QCryptoBlockOptionsLUKS `json:"-"` } func (s QCryptoBlockOpenOptions) MarshalJSON() ([]byte, error) { var bytes []byte var err error if s.Qcow != nil { tmp := struct { QCryptoBlockOptionsQCow Discriminator string `json:"format"` }{ QCryptoBlockOptionsQCow: *s.Qcow, Discriminator: "qcow", } if bytes, err = json.Marshal(tmp); err != nil { return nil, err } } if s.Luks != nil { if len(bytes) != 0 { return nil, errors.New(`multiple fields set for QCryptoBlockOpenOptions`) } tmp := struct { QCryptoBlockOptionsLUKS Discriminator string `json:"format"` }{ QCryptoBlockOptionsLUKS: *s.Luks, Discriminator: "luks", } if bytes, err = json.Marshal(tmp); err != nil { return nil, err } } if len(bytes) == 0 { return nil, errors.New(`null not supported for QCryptoBlockOpenOptions`) } return bytes, nil } func (s *QCryptoBlockOpenOptions) UnmarshalJSON(data []byte) error { tmp := struct { Discriminator string `json:"format"` }{} if err := json.Unmarshal(data, &tmp); err != nil { return err } switch tmp.Discriminator { case "qcow": s.Qcow = &QCryptoBlockOptionsQCow{} if err := json.Unmarshal(data, s.Qcow); err != nil { s.Qcow = nil return err } case "luks": s.Luks = &QCryptoBlockOptionsLUKS{} if err := json.Unmarshal(data, s.Luks); err != nil { s.Luks = nil return err } } return nil } func main() { jsonLuks := `{"key-secret":"my luks secret is here","format":"luks"}` jsonQcow := `{"key-secret":"my qcow secret is here","format":"qcow"}` r := QCryptoBlockOpenOptions{} if err := json.Unmarshal([]byte(jsonLuks), &r); err != nil { panic(err) } else if r.Luks == nil || r.Qcow != nil { panic(fmt.Sprintf("Wrong: %v", r)) } else if b, err := json.Marshal(&r); err != nil { panic(err) } else if string(b) != jsonLuks { panic(string(b)) } r = QCryptoBlockOpenOptions{} if err := json.Unmarshal([]byte(jsonQcow), &r); err != nil { panic(err) } else if r.Luks != nil || r.Qcow == nil { panic(fmt.Sprintf("Wrong: %v", r)) } else if b, err := json.Marshal(&r); err != nil { panic(err) } else if string(b) != jsonQcow { panic(string(b)) } r = QCryptoBlockOpenOptions{} if _, err := json.Marshal(&r); err == nil { panic("No fields set should be an error") } else if !strings.Contains(err.Error(), "null not supported") { panic(err) } qcowSecret := "my-qcow-secret-is-here" luksSecret := "my-luks-secret-is-here" r = QCryptoBlockOpenOptions{ Qcow: &QCryptoBlockOptionsQCow{ KeySecret: &qcowSecret, }, Luks: &QCryptoBlockOptionsLUKS{ KeySecret: &luksSecret, }, } if _, err := json.Marshal(&r); err == nil { panic("multiple fields set should be an error") } else if !strings.Contains(err.Error(), "multiple fields set for") { panic(err) } }