Parse an Object Map from JSON
Do It Without serde
A valid JSON object hash does not start with a hash character #
while a Rhai object map does.
That’s the only difference!
The syntax for an object map is extremely similar to the JSON representation of a object hash,
with the exception of null
values which can technically be mapped to ()
.
Use the Engine::parse_json
method to parse a piece of JSON into an object map.
// JSON string - notice that JSON property names are always quoted
// notice also that comments are acceptable within the JSON string
let json = r#"{
"a": 1, // <- this is an integer number
"b": true,
"c": 123.0, // <- this is a floating-point number
"$d e f!": "hello", // <- any text can be a property name
"^^^!!!": [1,42,"999"], // <- value can be array or another hash
"z": null // <- JSON 'null' value
}"#;
// Parse the JSON expression as an object map
// Set the second boolean parameter to true in order to map 'null' to '()'
let map = engine.parse_json(json, true)?;
map.len() == 6; // 'map' contains all properties in the JSON string
// Put the object map into a 'Scope'
let mut scope = Scope::new();
scope.push("map", map);
let result = engine.eval_with_scope::<i64>(&mut scope, r#"map["^^^!!!"].len()"#)?;
result == 3; // the object map is successfully used in the script
The JSON text must represent a single object hash – i.e. must be wrapped within braces
{
…}
.
It cannot be a primitive type (e.g. number, string etc.). Otherwise it cannot be converted into an object map and a type error is returned.
JSON numbers are all floating-point while Rhai supports integers (INT
) and floating-point (FLOAT
)
(except under no_float
).
Most common generators of JSON data distinguish between integer and floating-point values by always
serializing a floating-point number with a decimal point (i.e. 123.0
instead of 123
which is
assumed to be an integer).
This style can be used successfully with Rhai object maps.
Sub-objects are handled transparently by Engine::parse_json
.
It is not necessary to replace {
with #{
in order to fake a Rhai object map literal.
// JSON with sub-object 'b'.
let json = r#"{"a":1, "b":{"x":true, "y":false}}"#;
// 'parse_json' handles this just fine.
let map = engine.parse_json(json, false)?;
// 'map' contains two properties: 'a' and 'b'
map.len() == 2;
Internally, Engine::parse_json
cheats by treating the JSON text as a Rhai script.
That is why it even supports comments and arithmetic expressions in the JSON text, although it is not a good idea to rely on non-standard JSON formats.
A token remap filter is used to convert {
into #{
and null
to ()
.
Use serde
See Serialization/ Deserialization of Dynamic
with serde
for more details.
Remember, Engine::parse_json
is nothing more than a cheap alternative to true JSON parsing.
If strict correctness is needed, or for more configuration possibilities, turn on the
serde
feature to pull in serde
which enables
serialization and deserialization to/from multiple formats, including JSON.
Beware, though… the serde
crate is quite heavy.