#I cant get my user unmarshaler to work for a type inside a slice.

1 messages · Page 1 of 1 (latest)

old depot
#

It parses the first value fine, but the second one it just appears to skip it:
this is a minimul standalone example:

objects := `
{
  "id": "1",
  "objects": [
    {
      "name": "object 1",
      "type": "a"
    },
    {
      "name": "object 2",
      "type": "b"
    }
  ],
}
`

JsonFile :: struct {
    id:      string,
    objects: []Obj,
}
Obj :: struct {
    name: string,
    type: Type,
}
Type :: enum {
    TypeInvalid,
    TypeA,
    TypeB,
}
main :: proc() {
    json.set_user_unmarshalers(new(map[typeid]json.User_Unmarshaler))
    reg_err := json.register_user_unmarshaler(Type, type_unmarshal)

    json_file: JsonFile
    err := json.unmarshal_string(objects, &json_file)

    for obj in json_file.objects {
        fmt.println(obj)
    }
}
type_unmarshal :: proc(p: ^json.Parser, v: any) -> json.Unmarshal_Error {
    token := p.curr_token.text
    switch token {
        case `"a"`:
            (^Type)(v.data)^ = .TypeA
        case `"b"`:
            (^Type)(v.data)^ = .TypeB
        case:
            (^Type)(v.data)^ = .TypeInvalid
    }
    return .None
}
#

I would expect the output to contain:
Obj{name = "object 1", type = "TypeA"}
Obj{name = "object 2", type = "TypeB"}

But instead i get this:
Obj{name = "object 1", type = "TypeA"}
Obj{name = "", type = "TypeInvalid"}

odd trellis
#

I see two things:

  1. The user unmarshalers work directly with the parser, meaning you need to tell it what tokens you've used. json.expect_token(p, .String) or_return is an easy way to check that the token is actually a string and consume it if so, but produce an error if not. This does advance the parser, moving the string's token to p.prev_token. If you want to handle other types too (e.g. also allow an integer value), you may want more sophisticated logic for this.
  2. json.Unmarshal_Error is a union not marked #shared_nil, so returning .None isn't actually equivalent to nil (which is what's expected for "no error"). return nil instead. json.Unmarshal_Error should probably be marked #shared_nil so that this works as expected, though.

A fixed variant (with other not-directly-relevant changes):

type_unmarshal :: proc(p: ^json.Parser, v: any) -> json.Unmarshal_Error {
    json.expect_token(p, .String) or_return
    t := &v.(Type)
    switch p.prev_token.text {
        case `"a"`: t^ = .TypeA
        case `"b"`: t^ = .TypeB
    }
    return nil
}
old depot
#

ah yes that was it! I didnt advance the token. I tried following the example on github:
json.advance_token(p)
Why does the example not advance a token? or is it just not working either?
Also for the return value .None, also got it from there, i already got some weird behaviour because of that. nil makes more sense, thanks!

GitHub

Odin Programming Language. Contribute to odin-lang/Odin development by creating an account on GitHub.

odd trellis
#

Yeah, it's wrong. It "looks" right there at a glance because it sets the only value in the struct and returns .None , which is detected as an error and causes the unmarshaling to stop. But None doesn't look like an error

#

If you change it just a little bit, you see it doing the same thing yours was:

data := `{"value":101010, "second": 101}`
SomeType :: struct {
  value: int,
  second: int,
}
// ...
fmt.println(y, unmarshal_err) // SomeType{value = 42, second = 0} None
// what you actually want is SomeType{value = 42, second = 5} nil