Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse JSON value as Enum in Rust

Tags:

rust

I have enum that wraps primitives:

#[deriving(Decodable)]
enum MyValue {
  MyString(String),
  MyF64(f64),
  MyF32(f32),
  MyI64(i64),
  MyI32(i32),
  ...
  ...
}

Now I want to convert a JSON string into corresponding variant of MyValue, for instance:

let v: MyValue = parse_json("3.14") // MyF32(3.14f32)
let v: MyValue = parse_json("3.14151515151515") // MyF64(3.14151515151515f64)
let v: MyValue = parse_json("42") // MyI32(42)
let v: MyValue = parse_json("\"hello\"") // MyString("hello")

If I do it directly:

let my_value: MyValue = json::decode("3.14").unwrap();

I get:

task '<main>' failed at 'calledResult::unwrap()on anErrvalue: ExpectedError(String or Object, 3.14)'

Is it possible to do above with only Rust type system?

If not, what is the approach that should be taken? Should I use regexes/PEG parser or something else?

playpen

like image 686
Valentin V Avatar asked May 15 '26 18:05

Valentin V


1 Answers

I doubt you'll be able to do this with #[deriving(Decodable)]. Here is a fragment produced by rustc --pretty expanded:

__arg_0.read_enum("MyValue", |_d|
    _d.read_enum_variant(["MyString", "MyF64", "MyF32", "MyI64", "MyI32"], |_d, i|
        ::std::result::Ok(match i {
            0u => MyString(match _d.read_enum_variant_arg(0u, |_d| ::serialize::Decodable::decode(_d)) {
                Ok(__try_var) => __try_var,
                Err(__try_var) => return Err(__try_var),
            }),
            1u => MyF64(match _d.read_enum_variant_arg(0u, |_d| ::serialize::Decodable::decode(_d)) {
                Ok(__try_var) => __try_var,
                Err(__try_var) => return Err(__try_var),
            }),
            2u => ...

That is, it dispatches on enum variant index provided by the decoder. In fact, compatible JSON would look like this, you can see it yourself:

{ "variant": 1, "fields": [12345] }  // corresponds to MyF64(12345)

In general I see no way to do what you want in any language, not only in Rust. JSON is just not sophisticated enough to distinguish between different kinds of integers, for example. Also Rust Decodable trait requires you to determine exact variant of the enum you're decoding using some kind of metadata; you can't read arbitrary value there and decide on its type at runtime. So you need to write some kind of parser yourself, however, I strongly recommend reconsider what you are doing. JSON is likely not the language you need here.

like image 100
Vladimir Matveev Avatar answered May 17 '26 14:05

Vladimir Matveev