Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Serialization and Deserialization of Dynamic with serde

Rhai’s Dynamic type supports serialization and deserialization by serde via the serde feature.

Tip

Dynamic works both as a serialization format as well as a data type that is serializable.

Serialize/Deserialize a Dynamic

With the serde feature turned on, Dynamic implements serde::Serialize and serde::Deserialize, so it can easily be serialized and deserialized with serde (for example, to and from JSON via serde_json).

let value: Dynamic = ...;

// Serialize 'Dynamic' to JSON
let json = serde_json::to_string(&value);

// Deserialize 'Dynamic' from JSON
let result: Dynamic = serde_json::from_str(&json);

Custom types

Custom types are serialized as text strings of the value’s type name.

BLOB’s

BLOB’s, or byte-arrays, are serialized and deserialized as simple arrays for some formats such as JSON.

Tip: Lighter alternative for JSON

The serde_json crate is quite heavy.

If only simple JSON parsing (i.e. only deserialization) of a hash object into a Rhai object map is required, the Engine::parse_json method is available as a cheap alternative, but it does not provide the same level of correctness, nor are there any configurable options.

Dynamic as Serialization Format

A Dynamic can be seamlessly converted to and from any type that implements serde::Serialize and/or serde::Deserialize, acting as a serialization format.

Serialize Any Type to Dynamic

The function rhai::serde::to_dynamic automatically converts any Rust type that implements serde::Serialize into a Dynamic.

For primary types, this is usually not necessary because using Dynamic::from is much easier and is essentially the same thing. The only difference is treatment for integer values. Dynamic::from keeps different integer types intact, while rhai::serde::to_dynamic converts them all into INT (i.e. the system integer type which is i64 or i32 depending on the only_i32 feature).

Rust struct’s (or any type that is marked as a serde map) are converted into object maps while Rust Vec’s (or any type that is marked as a serde sequence) are converted into arrays.

While it is also simple to serialize a Rust type to JSON via serde, then use Engine::parse_json to convert it into an object map, rhai::serde::to_dynamic serializes it to Dynamic directly via serde without going through the JSON step.

use rhai::{Dynamic, Map};
use rhai::serde::to_dynamic;

#[derive(Debug, serde::Serialize)]
struct Point {
    x: f64,
    y: f64
}

#[derive(Debug, serde::Serialize)]
struct MyStruct {
    a: i64,
    b: Vec<String>,
    c: bool,
    d: Point
}

let x = MyStruct {
    a: 42,
    b: vec![ "hello".into(), "world".into() ],
    c: true,
    d: Point { x: 123.456, y: 999.0 }
};

// Convert the 'MyStruct' into a 'Dynamic'
let map: Dynamic = to_dynamic(x);

map.is::<Map>() == true;

Deserialize a Dynamic into Any Type

The function rhai::serde::from_dynamic automatically converts a Dynamic value into any Rust type that implements serde::Deserialize.

In particular, object maps are converted into Rust struct’s (or any type that is marked as a serde map) while arrays are converted into Rust Vec’s (or any type that is marked as a serde sequence).

use rhai::{Engine, Dynamic};
use rhai::serde::from_dynamic;

#[derive(Debug, serde::Deserialize)]
struct Point {
    x: f64,
    y: f64
}

#[derive(Debug, serde::Deserialize)]
struct MyStruct {
    a: i64,
    b: Vec<String>,
    c: bool,
    d: Point
}

let engine = Engine::new();

let result: Dynamic = engine.eval(
r#"
    #{
        a: 42,
        b: [ "hello", "world" ],
        c: true,
        d: #{ x: 123.456, y: 999.0 }
    }
"#)?;

// Convert the 'Dynamic' object map into 'MyStruct'
let x: MyStruct = from_dynamic(&result)?;

Cannot deserialize shared values

A Dynamic containing a shared value cannot be deserialized. It will give a type error.

Use Dynamic::flatten to obtain a cloned copy before deserialization (if the value is not shared, it is simply returned and not cloned).

Shared values are turned off via the no_closure feature.