Json: Can serde deserialize an arbitrary json structure into a recursive enum/tree?

Created on 27 Aug 2016  路  4Comments  路  Source: serde-rs/json

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.

support

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::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();

All 4 comments

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);
    }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredforth picture jaredforth  路  4Comments

brundonsmith picture brundonsmith  路  3Comments

dtolnay picture dtolnay  路  3Comments

mfarrugi picture mfarrugi  路  4Comments

NoraCodes picture NoraCodes  路  6Comments