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
}