Skip to content

Conversation

MalteJanz
Copy link

@MalteJanz MalteJanz commented Oct 5, 2025

My first attempt at closing #3474

  • modified unmarshal_object to return an error if no field at all was parsed / assigned to the struct
  • that causes unmarshal_value to iterate over the remaining union variants until one succeeds or runs out of variants:
    for variant, i in u.variants {
    variant_any := any{v.data, variant.id}
    variant_p := p^
    if err = unmarshal_value(&variant_p, variant_any); err == nil {
    p^ = variant_p
    raw_tag := i
    if !u.no_nil { raw_tag += 1 }
    tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
    assign_int(tag, raw_tag)
    return
    }
    }
    return UNSUPPORTED_TYPE

Open questions / not considered yet:

  • what happens if two structs as different union variants have overlapping field names? It likely chooses the first one which is still not ideal
  • does the parser advance in case it encounters non existing fields (to skip them)? So far I don't think so and it didn't consume the b json key while trying to parse the A struct. Not sure if this is true for the general case
  • Is there a better way to try parsing all union variants and only accept the one that matched the best to the available data (ideally the struct that contained the most matching fields out of its total fields)?
  • this is a break in unmarshaling behaviour and definitly needs more testing to not break something unintended.

Anyone feel free to pick this up, I'm just getting started with odin and there might be better ways to solve this issue 🙂

Reproduction example that is fixed with this change

package main

import "core:encoding/json"
import "core:fmt"

A :: struct {
    a: int,
}

B :: struct {
    b: string,
}

U :: union {
    rawptr,
    A,
    B,
}

main :: proc() {
    original: U = B {
        b = "Hellope"
    }

    json_data, err := json.marshal(original)
    assert(err == nil)
    fmt.printfln("original: %v", string(json_data))

    read : U
    error := json.unmarshal(json_data, &read)
    fmt.printfln("read: %#v", read)

    assert(error == nil)
    assert(read.(B).b == "Hellope")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant