rustdeserializationserde

Force Rust serde to deserialize JSON Number to u16


How can I force serde to parse a JSON Number as a Rust u16 type?

Below I parse a JSON file. json_data is of type Value. This only has a as_u64() method, no as_u16(). As a result I first have to parse the JSON Number as a u64, then try and parse that as u16. This seems fairly convoluted. Is there some way I can force serde_json to parse this value as a u16 to avoid this?

(As a side note, the JSON data has already been validated using a JSON schema, which checks the value of x is within the bounds of u16).

$ cat test.json
{
    "x": 100
}
use serde_json::Value;
use std::fs::File;
use std::io::BufReader;

pub struct Test {
    pub x: u16,
}

fn main() {
    let filename = "test.json";
    let file =
        File::open(filename).unwrap_or_else(|e| panic!("Unable to open file {}: {}", filename, e));
    let reader = BufReader::new(file);
    let json_data: Value = serde_json::from_reader(reader)
        .unwrap_or_else(|e| panic!("Unable to parse JSON file {}: {}", filename, e));

    assert!(json_data["x"].is_u64());

    let mut t = Test { x: 5 };

    if json_data["x"].is_u64() {
        let _new_x: u64 = json_data["x"].as_u64().expect("Unable to parse x");
        t.x = u16::try_from(_new_x).expect("Unable to convert x to u16");
    }
}

Solution

  • Don't use the raw serde_json::Value type for this usecase.

    While it is possible, it is not what serde is intended for; instead, serde is meant to deserialize directly into your struct.

    The magic here is adding a #[derive(Deserialize)] to your struct. Be aware that you need to activate the derive feature of serde for this.

    Like so:

    use serde::Deserialize;
    use std::fs::File;
    use std::io::BufReader;
    
    #[derive(Debug, Deserialize)]
    pub struct Test {
        pub x: u16,
    }
    
    fn main() {
        let filename = "test.json";
        let file =
            File::open(filename).unwrap_or_else(|e| panic!("Unable to open file {}: {}", filename, e));
        let reader = BufReader::new(file);
        let json_data: Test = serde_json::from_reader(reader)
            .unwrap_or_else(|e| panic!("Unable to parse JSON file {}: {}", filename, e));
    
        println!("{:?}", json_data)
    }
    
    Test { x: 100 }
    

    If you now change the test.json to:

    {
        "x": 123456
    }
    

    You get:

    thread 'main' panicked at src\main.rs:16:29:
    Unable to parse JSON file test.json: invalid value: integer `123456`, expected u16 at line 2 column 16