Actix Json Deserializing Enum Type Variants

I'm relatively new to Rust still but I'm trying to allow multiple type inputs for actix::web::Json.

The enum:

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum AreaInput {
    Text(String),
    SingleArray(Vec<[f64; 2]>),
    MultiArray(Vec<Vec<[f64; 2]>>),
    SingleStruct(Vec<LatLon>),
    MultiStruct(Vec<Vec<LatLon>>),
}

The type passed into actix::web::Json<RouteGeneration>

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RouteGeneration {
    pub area: Option<AreaInput>,
    pub fast: Option<bool>,
}

Normalizing the input:

pub fn area_input(area: AreaInput) -> (Vec<Vec<[f64; 2]>>, ReturnType) {
    match area {
        AreaInput::Text(area) => (arrays::parse_flat_text(area.as_str()), ReturnType::Text),
        AreaInput::SingleArray(area) => (vec![area], ReturnType::SingleArray),
        AreaInput::MultiArray(area) => (area, ReturnType::MultiArray),
        AreaInput::SingleStruct(area) => {
            (vec![arrays::coord_to_array(area)], ReturnType::SingleStruct)
        }
        AreaInput::MultiStruct(area) => (
            area.into_iter().map(arrays::coord_to_array).collect(),
            ReturnType::MultiStruct,
        ),
    }
}

I'm not 100% sure if this is the "correct" way to handle a value that could have multiple types or if it is and there's just an extra step I need to do since this is going through Actix web's json deserialization first.

Example route:

#[post("/bootstrap")]
async fn bootstrap(
    conn: web::Data<DatabaseConnection>,
    scanner_type: web::Data<String>,
    payload: web::Json<RouteGeneration>,
) -> Result<HttpResponse, Error> {
    let RouteGeneration {
        area,
        fast,
    } = payload.into_inner();
    let (area, default_return_type) =
        normalize::area_input(area.unwrap_or(AreaInput::SingleArray(vec![])));

// ...do more stuff
}

If I pass in any sort of string for area, I get:

Json deserialize error: unknown variant `1.0 2.0, 2.0 3.0`, expected one of `Text`, `SingleArray`, `MultiArray`, `SingleStruct`, `MultiStruct` at line 3 column 28

If I pass in any kind of the "supported" arrays, I get:

Json deserialize error: expected value at line 3 column 11

Everything compiles fine so I'm guessing this is just something I need to do differently with actix to account for an enum input type.

Thanks!

An enum has to know which variant to create when deserializing itself. Therefore, the discriminant (i.e. the variant name) must be present in the input. You can't just pass a String an expect the AreaInput to know that it is supposed to deserialize the first variant.

Therefore, the default representation for enums in Serde is a map, where the key is the name of the variant, and the corresponding value is the associated data.

You can change to the "untagged" representation by adding #[serde(untagged)] to your enum, which will switch to a more magical (and thus more error-prone!) mechanism, whereby it will attempt to deserialize into the associated data of each variant in order, and it will return the first one that succeeded.

Ah, you're a hero, thank you! Changing to the untagged representation did indeed "solve" the problem but I'll definitely look at switching to a mapped/keyed model instead.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.