Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can not parse enum number #1162

Closed
iddm opened this issue Feb 23, 2018 · 4 comments
Closed

Can not parse enum number #1162

iddm opened this issue Feb 23, 2018 · 4 comments
Labels

Comments

@iddm
Copy link

iddm commented Feb 23, 2018

I have tried to parse a enum with numbers and it did not work for me (playground).

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::fmt;
use serde::de::{self, Deserialize, Deserializer, Visitor};

macro_rules! enum_number {
    ($name:ident { $($variant:ident = $value:expr, )* }) => {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        pub enum $name {
            $($variant = $value,)*
        }

        impl<'de> serde::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where D: serde::Deserializer<'de>
            {
                struct Visitor;

                impl<'de> serde::de::Visitor<'de> for Visitor {
                    type Value = $name;

                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        formatter.write_str("integer")
                    }

                    fn visit_i64<E>(self, value: i64) -> Result<$name, E>
                        where E: serde::de::Error
                    {
                        // Rust does not come with a simple way of converting a
                        // number to an enum, so use a big `match`.
                        match value {
                            $( $value => Ok($name::$variant), )*
                            _ => Err(E::custom(
                                format!("unknown {} value: {}",
                                stringify!($name), value))),
                        }
                    }
                }

                // Deserialize the enum from a i64.
                deserializer.deserialize_i64(Visitor)
            }
        }
    }
}

enum_number!(Change {
    Up = 1,
    None = 0,
    Down = -1,
});


fn main() {
    let j = "1";
    println!("{:?}", serde_json::from_str::<Change>(j).unwrap());
}

The error is very strange:

thread 'main' panicked at 'called Result::unwrap() on an Err value: ErrorImpl { code: Message("invalid type: integer 1, expected integer"), line: 1, column: 1 }', /checkout/src/libcore/result.rs:916:5

What's wrong?

  • serde 1.0

P.S. Even original docs in serde.rs fail if I change u64 to i64 in the deserialize method (playground).

@dtolnay
Copy link
Member

dtolnay commented Feb 23, 2018

That error message is pretty bad...

I think what you're seeing is JSON does not differentiate between different number types and as a compromise between JSON's notion of numbers and Rust's notion of numbers serde_json treats all nonnegative integers as u64 and all negative integers as i64. It should work if we also provide a visit_u64 method so that both nonnegative and negative integers end up applying the same match.

fn visit_u64<E>(self, value: u64) -> Result<$name, E>
    where E: serde::de::Error
{
    self.visit_i64(value as i64)
}

@iddm
Copy link
Author

iddm commented Feb 23, 2018

@dtolnay yes, this works. However, this is not understandable at a glance. I suggest you commenting this behavior on the same serde.rs page and even change the reference macro_rules! implementation there to work with i64. I may try doing such a PR if you like.

@iddm
Copy link
Author

iddm commented Feb 23, 2018

@dtolnay I am going to check whether it is possible to do this via a procedural macro. If it is so, I'd do this in my serde_aux crate for easing the pain.

https://docs.rs/serde-aux/0.3.0/serde_aux/macro.serde_aux_enum_number_declare.html

However, of course, it should be done as a procedural macro in my opinion.

@dtolnay
Copy link
Member

dtolnay commented Mar 3, 2018

Thanks! I filed serde-rs/serde-rs.github.io#69 to follow up on showing how to handle a mix of positive and negative enum values in that example.

@dtolnay dtolnay closed this as completed Mar 3, 2018
@dtolnay dtolnay added the support label Mar 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants