Typed parsing of a part of the JSON input

In a contrived example below, I look at the "type" field of the JSON, and based on its value, I parse the remainder of the JSON. (Pretty common scenario for any kind of JSON-based API).
The code below fails with: cannot move out of borrowed content. I am a newb, and I fully admit that I don't fully understand how lifetimes in serde work.

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
}

fn main() -> Result<()> {
    let data = r#"
        {
            "type": "person",
            "person":{
                "name": "John Smith", 
                "age": 33
            }
        }"#;

    let data: serde_json::Value = serde_json::from_str(data)?;

    let data_type: String = data["type"].to_string();

    if data["type"] == "person" {
        let person = data["person"]; // error: cannot move out of borrowed content
        let p: Person = serde_json::from_value(person)?;
    }

    Ok(())
}

I am also beginner who has fought with compiler for a while. That problematic line basically tries to get ownership of data[“person”]. You can try to clone data[“person”].clone()

@laiboonh is correct; indexing into a serde_json::Value gives you a reference to a Value, which is why you can't move it (it's referencing local data.) You could clone it; you could also take it out directly, if you don't want to do anything else with it:

let mut data: serde_json::Value = serde_json::from_str(data)?;

// ...

if data["type"] == "person" {
    let person_value = data
        .as_object_mut()
        .and_then(|o| o.remove("person"))
        .unwrap();
    let person: Person = serde_json::from_value(person)?;
}

However, serde can do this automatically for you; if you create an enum that has Person as a possible value, and set serde to use a tagged representation for that enum, you shouldn't need to check for the type at all:

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
enum Data {
    Person { person: Person },
}

// ...

let data: Data = serde_json::from_str(data)?;
if let Data::Person { person } = data {
    // got a person
}
3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.