Search code examples
jsonrustserde

serde/rust deserialize JSON into Hashmap


How to properly define the data types such that the JSON de-serialize works fine with Serde/Rust for the following examples? I believe the Hashmap is the culprit somehow. Unable to tell what precisely is wrong. I hope for more complex examples involving logical and comparison operators to work as well.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

pub type Single = MetadataColumnValue;
pub type Multiple = Vec<MetadataColumnValue>;

// Define the generic MetadataColumn type
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum MetadataColumnValue {
    StringValue(String),
    IntValue(i32),
    FloatValue(f64),
    // Add other types as needed
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum ComparisonOperator {
    #[serde(rename = "$eq")]
    Eq(Single),

    #[serde(rename = "$ne")]
    Ne(Single),

    #[serde(rename = "$gt")]
    Gt(Single),

    #[serde(rename = "$gte")]
    Gte(Single),

    #[serde(rename = "$ge")] // Assuming $ge is the same as $gte for this example
    Ge(Single),

    #[serde(rename = "$lt")]
    Lt(Single),

    #[serde(rename = "$lte")]
    Lte(Single),

    #[serde(rename = "$in")]
    In(Multiple),

    #[serde(rename = "$nin")]
    Nin(Multiple),
    // Add other operators as needed
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum LogicalOperator {
    #[serde(rename = "$and")]
    And(Vec<Filter>),

    #[serde(rename = "$or")]
    Or(Vec<Filter>),
    // Add other logical operators as needed
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum Filter {
    Comparison {
        #[serde(flatten)]
        column: HashMap<String, ComparisonOperator>,
    },
    Logical(LogicalOperator),
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct SomeThing {
    pub name: String,
    pub filter: Option<Filter>,
    pub count: Option<i32>,
}

fn main() {
    let json_data1 = r#"
    {
        "name": "example",
        "filter": {
            "genre": { "$eq": "drama" }
        },
        "count": 10
    }
    "#;

    let json_data2 = r#"
    {
        "name": "example",
        "filter": {
            "year": { "$ge": 2020 }
        },
        "count": 5
    }
    "#;

    let ann1: SomeThing = serde_json::from_str(json_data1).unwrap();
    let ann2: SomeThing = serde_json::from_str(json_data2).unwrap();

    println!("{:?}", ann1);
    println!("{:?}", ann2);
}
   Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.42s
     Running `target/debug/playground`
thread 'main' panicked at src/main.rs:98:60:
called `Result::unwrap()` on an `Err` value: Error("data did not match any variant of untagged enum Filter", line: 6, column: 9)

Solution

  • ComparisonOperator must be tagged. So just remove untagged from ComparisonOperator and it will work:

    #[derive(Serialize, Deserialize, Debug, PartialEq)]
    pub enum ComparisonOperator {
    }
    

    Read more on Enum representations for serde.