From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Andrea Bolognani <abologna@redhat.com>
Cc: Victor Toso <victortoso@redhat.com>,
qemu-devel@nongnu.org, Eric Blake <eblake@redhat.com>,
Markus Armbruster <armbru@redhat.com>,
John Snow <jsnow@redhat.com>
Subject: Re: [RFC PATCH v2 4/8] qapi: golang: Generate qapi's union types in Go
Date: Tue, 5 Jul 2022 17:35:26 +0100 [thread overview]
Message-ID: <YsRoTs/Ev+MPiIoN@redhat.com> (raw)
In-Reply-To: <CABJz62P_Fy=eyn-QjhOBSvTs_YRgMA=2=teeQwN9SsYGNKGLcQ@mail.gmail.com>
On Tue, Jul 05, 2022 at 08:45:30AM -0700, Andrea Bolognani wrote:
> On Fri, Jun 17, 2022 at 02:19:28PM +0200, Victor Toso wrote:
> > qapi:
> > | { 'union': 'SetPasswordOptions',
> > | 'base': { 'protocol': 'DisplayProtocol',
> > | 'password': 'str',
> > | '*connected': 'SetPasswordAction' },
> > | 'discriminator': 'protocol',
> > | 'data': { 'vnc': 'SetPasswordOptionsVnc' } }
> >
> > go:
> > | type SetPasswordOptions struct {
> > | // Base fields
> > | Password string `json:"password"`
> > | Connected *SetPasswordAction `json:"connected,omitempty"`
> > |
> > | // Variants fields
> > | Vnc *SetPasswordOptionsVnc `json:"-"`
> > | }
>
> Generated code:
>
> > type GuestPanicInformation struct {
> > // Variants fields
> > HyperV *GuestPanicInformationHyperV `json:"-"`
> > S390 *GuestPanicInformationS390 `json:"-"`
> > }
> >
> > func (s GuestPanicInformation) MarshalJSON() ([]byte, error) {
> > type Alias GuestPanicInformation
> > alias := Alias(s)
> > base, err := json.Marshal(alias)
> > if err != nil {
> > return nil, err
> > }
> >
> > driver := ""
> > payload := []byte{}
> > if s.HyperV != nil {
> > driver = "hyper-v"
> > payload, err = json.Marshal(s.HyperV)
> > } else if s.S390 != nil {
> > driver = "s390"
> > payload, err = json.Marshal(s.S390)
> > }
> >
> > if err != nil {
> > return nil, err
> > }
> >
> > if len(base) == len("{}") {
> > base = []byte("")
> > } else {
> > base = append([]byte{','}, base[1:len(base)-1]...)
> > }
> >
> > if len(payload) == 0 || len(payload) == len("{}") {
> > payload = []byte("")
> > } else {
> > payload = append([]byte{','}, payload[1:len(payload)-1]...)
> > }
> >
> > result := fmt.Sprintf(`{"type":"%s"%s%s}`, driver, base, payload)
> > return []byte(result), nil
>
> All this string manipulation looks sketchy. Is there some reason that
> I'm not seeing preventing you for doing something like the untested
> code below?
>
> func (s GuestPanicInformation) MarshalJSON() ([]byte, error) {
> if s.HyperV != nil {
> type union struct {
> Discriminator string `json:"type"`
> HyperV GuestPanicInformationHyperV `json:"hyper-v"`
> }
> tmp := union {
> Discriminator: "hyper-v",
> HyperV: s.HyperV,
> }
> return json.Marshal(tmp)
> } else if s.S390 != nil {
> type union struct {
> Discriminator string `json:"type"`
> S390 GuestPanicInformationHyperV `json:"s390"`
> }
> tmp := union {
> Discriminator: "s390",
> S390: s.S390,
> }
> return json.Marshal(tmp)
> }
> return nil, errors.New("...")
> }
Using these dummy structs is the way I've approached the
discriminated union issue in the libvirt Golang XML bindings
and it works well. It is the bit I like the least, but it was
the lesser of many evils, and on the plus side in the QEMU case
it'll be auto-generated code.
>
> > func (s *GuestPanicInformation) UnmarshalJSON(data []byte) error {
> > type Alias GuestPanicInformation
> > peek := struct {
> > Alias
> > Driver string `json:"type"`
> > }{}
> >
> > if err := json.Unmarshal(data, &peek); err != nil {
> > return err
> > }
> > *s = GuestPanicInformation(peek.Alias)
> >
> > switch peek.Driver {
> >
> > case "hyper-v":
> > s.HyperV = new(GuestPanicInformationHyperV)
> > if err := json.Unmarshal(data, s.HyperV); err != nil {
> > s.HyperV = nil
> > return err
> > }
> > case "s390":
> > s.S390 = new(GuestPanicInformationS390)
> > if err := json.Unmarshal(data, s.S390); err != nil {
> > s.S390 = nil
> > return err
> > }
> > }
> > // Unrecognizer drivers are silently ignored.
> > return nil
>
> This looks pretty reasonable, but since you're only using "peek" to
> look at the discriminator you should be able to leave out the Alias
> type entirely and perform the initial Unmarshal operation while
> ignoring all other fields.
Once you've defined the dummy structs for the Marshall case
though, you might as well use them for Unmarshall too, so you're
not parsing the JSON twice.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2022-07-05 16:50 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-17 12:19 [RFC PATCH v2 0/8] qapi: add generator for Golang interface Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 1/8] qapi: golang: Generate qapi's enum types in Go Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 2/8] qapi: golang: Generate qapi's alternate " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-08-17 14:04 ` Victor Toso
2022-08-19 16:27 ` Andrea Bolognani
2022-08-22 6:59 ` Victor Toso
2022-08-29 11:27 ` Markus Armbruster
2022-08-29 13:31 ` Victor Toso
2022-09-02 14:49 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 3/8] qapi: golang: Generate qapi's struct " Victor Toso
2022-06-17 14:41 ` Daniel P. Berrangé
2022-06-17 15:23 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 4/8] qapi: golang: Generate qapi's union " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:35 ` Daniel P. Berrangé [this message]
2022-07-06 9:28 ` Andrea Bolognani
2022-07-06 9:37 ` Daniel P. Berrangé
2022-07-06 9:48 ` Daniel P. Berrangé
2022-07-06 12:20 ` Andrea Bolognani
2022-08-17 16:25 ` Victor Toso
2022-08-19 7:20 ` Victor Toso
2022-08-19 15:25 ` Andrea Bolognani
2022-08-22 6:33 ` Victor Toso
2022-08-17 16:06 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 5/8] qapi: golang: Generate qapi's event " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:47 ` Daniel P. Berrangé
2022-07-06 14:53 ` Andrea Bolognani
2022-07-06 15:07 ` Daniel P. Berrangé
2022-07-06 16:22 ` Andrea Bolognani
2022-08-18 7:55 ` Victor Toso
2022-08-18 7:47 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 6/8] qapi: golang: Generate qapi's command " Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 7/8] qapi: golang: Add CommandResult type to Go Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-07-05 16:49 ` Daniel P. Berrangé
2022-08-17 15:00 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 8/8] qapi: golang: document skip function visit_array_types Victor Toso
2022-06-27 7:15 ` [RFC PATCH v2 0/8] qapi: add generator for Golang interface Markus Armbruster
2022-06-27 12:48 ` Victor Toso
2022-06-27 15:29 ` Markus Armbruster
2022-08-18 8:04 ` Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-08-17 14:24 ` Victor Toso
2022-08-29 11:53 ` Markus Armbruster
2022-08-29 14:05 ` Victor Toso
2024-11-07 10:43 ` Daniel P. Berrangé
2024-11-07 12:36 ` Markus Armbruster
2024-11-07 13:06 ` Daniel P. Berrangé
2024-11-07 13:35 ` Daniel P. Berrangé
2024-11-07 14:18 ` Markus Armbruster
2024-11-08 9:43 ` Victor Toso
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YsRoTs/Ev+MPiIoN@redhat.com \
--to=berrange@redhat.com \
--cc=abologna@redhat.com \
--cc=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=jsnow@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=victortoso@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).