For example,
https://github.com/rust-lang-nursery/rustc-serialize/blob/master/src/json.rs#L261-L270
pub enum Json {
I64(i64),
U64(u64),
F64(f64),
String(string::String),
Boolean(bool),
Array(self::Array),
Object(self::Object),
Null,
}
pub type Array = Vec<Json>;
pub type Object = BTreeMap<string::String, Json>;
I'd like to mix validated and free-form JSON in the same string.
Absolutely. Are you looking to use that specific one from rustc-serialize or can you use this one instead which works out of the box? serde_json::Value
You can embed serde_json::Value into serializable structs to deserialize that part of the struct into a recursive enum:
// Deserialize something like `{"id": "x", "body": /* any valid json */}`
#[derive(Serialize, Deserialize)]
struct MyStruct {
id: String,
body: serde_json::Value,
}
Then you can either use the enum directly, or if you want you can continue deserializing it further using serde_json::from_value:
let my_struct: MyStruct = serde_json::from_str(j).unwrap();
if my_struct.id == "x" {
let x: MyOtherStruct = serde_json::from_value(my_struct.body).unwrap();
/* ... */
}
EDIT: and to deserialize an entire JSON input to a serde_json::Value:
let value: serde_json::Value = serde_json::from_str(j).unwrap();
Thank you! 10 hours of reading docs didn't let me find this. Do you know if it's possible to 'pause' deserialization, to (for example), read a version number and swap out the remainder of the parsing/struct? Or does any kind of logic require a full manual implementation?
I filed https://github.com/serde-rs/serde-rs.github.io/issues/18 to add an example that uses serde_json::Value.
Any logic more advanced than what is supported by the built-in attributes is going to require implementing Deserialize yourself. In this case if you have something like {"version": 1, "value": (value of type 1)} and {"version": 2, "value": (value of type 2)} then you can deserialize to a struct that has version: u8, value: serde_json::Value, then look at the version and continue deserializing using serde_json::from_value with the correct type.
If you can say a bit more about what the JSON looks like and what you need the Rust data structure to look like, I can flesh out some of the details and possibly use it as the website example because this could be a pretty common use case.
Thaaaanks!
I'm parsing an abstract syntax tree produced from a python program. The json has an array with varied objects, and depending on the type, I have to parse it in a different way.
The json looks something like this:
{
"ast_type": "Module",
"body": [
{ "ast_type": "Assign",},
{ "ast_type": "FunctionDef",},
{ "ast_type": "Expr",},
]
}
(I omitted the rest of the information pertinent to each "ast_type". What matters is that they have different structures).
In order to parse it, I programmed a struct for the json, knowing that the body was going to be an array of unknown object structure:
#[derive(Serialize, Deserialize, Debug)]
struct PyBody {
ast_type: String,
col_offset: u8,
lineno: u8,
//targets: Vec<PyTarget>
}
#[derive(Serialize, Deserialize, Debug)]
struct PyFunction {
ast_type: String,
body: Vec<serde_json::Value>
}
For each PyBody "ast_type" I programmed a different struct: PyAssign, PyFunctionDef, PyExpr (omitted here), and this is how the rest of the program looks like
fn read_code_from_file<P: AsRef<Path>>(path: P) -> Result<PyFunction, Box<Error>> {
// Open the file in read-only mode.
let file = File::open(path)?;
// Read the JSON contents of the file as an instance of `User`.
let first_pass = serde_json::from_reader(file)?;
// Return the `User`.
Ok(first_pass)
}
fn main() {
let nodes = read_code_from_file("../client/file1.json").unwrap();
println!("Parsed {:?} nodes:\n", nodes.body.len());
//println!("{:#?}", nodes.body[0]);
//parse each node
for i in 0..nodes.body.len()
{
//parse each node
let n: PyBody = serde_json::from_value(nodes.body[i].clone()).unwrap();
match n.ast_type.as_ref() {
"Assign" => {
//println!("parsing Assign node:");
let assign: PyAssign = serde_json::from_value(nodes.body[i].clone()).unwrap();
println!("{:?}", assign.ast_type);
},
"FunctionDef" => {
//println!("parsing FunctionDef node:");
let function_def: PyFunctionDef = serde_json::from_value(nodes.body[i].clone()).unwrap();
println!("{:?}, {:?}", function_def.ast_type, function_def.name);
},
"Expr" => {
//println!("parsing Expr node:");
let expr: PyExpr = serde_json::from_value(nodes.body[i].clone()).unwrap();
println!("{:?}", expr.ast_type);
},
_ => println!("matched unknown node, no action taken: {:?}", n.ast_type)
}
//println!("{:?}", n);
}
}
Most helpful comment
Absolutely. Are you looking to use that specific one from rustc-serialize or can you use this one instead which works out of the box?
serde_json::ValueYou can embed
serde_json::Valueinto serializable structs to deserialize that part of the struct into a recursive enum:Then you can either use the enum directly, or if you want you can continue deserializing it further using
serde_json::from_value:EDIT: and to deserialize an entire JSON input to a
serde_json::Value: