Serialize Js data
Here's fun facts. let say we have this in Js
.
const u8 = new Uint8Array([16, 42]) // Uint8Array [16,42]
const u8_string = JSON.stringify(u8) // '{"0":16,"1":42}'
And want to send that u8
from Js
→ Wasm
→ Rust
.
Our options
- Send
Uint8Array
asJsValue
fromjs
and useserde_wasm_bindgen::from_value(js_value)
to cast to derivedserde
struct.
graph LR A["<code>new Uint8Array([16, 42])</code>"] --JsValue<br><code>UInt8Array</code>--> B["<code>serde_wasm_bindgen::from_value(js_value)</code>"] --"Struct<br><code>&[u8]</code>"--> C[<code>Rust</code>]
- Send
Array
asJsValue
fromjs
and usejs_value.into_serde()
to castserde
struct inRust
.
graph LR A["<code>[16, 42]</code>"] --"JsValue<br><code>[16,42]</code>"--> B["<code>js_value.into_serde()</code>"] --"Struct<br><code>&[u8]</code>"--> C[<code>Rust</code>]
- Send
JSON.stringify(new Uint8Array([16, 42]))
asString/&str
fromjs
and use serdedeserialize_with
.
graph LR A["<code>JSON.stringify(new Uint8Array([16, 42]))</code>"] --"String<br><code>'{'0':16,'1':42}'</code">--> B["<code>serde::deserialize_with</code>"] --"Struct<br><code>&[u8]</code>"--> C[<code>Rust</code>]
How to handle JSON stringify UInt8Array
?
Somehow an app (actually dApp
) tend to send us Uint8Array
as JSON
string like this (fall into case #3 👆)...
{
"signatures": [
{
"0": 16,
"1": 42
}
]
}
And yes you can convert it to JsValue
before sending to Wasm
.
const u8 = new Uint8Array([16, 42]) // Uint8Array [16,42]
const u8_string = JSON.stringify(u8) // '{"0":16,"1":42}'
const u8_map = JSON.parse(u8_string) // {0: 16, 1: 42}
const parsed_u8 = new Uint8Array(Object.values(u8_map)) // Uint8Array [16,42]
But if we tend to keep all logic in Rust
so we cry and accept JSON
as String
then convert it to u8
in Wasm
instead.
Deserialize
with serde
Warning:
unwrap
without handle is for POC or testing purpose.
use serde::Deserialize; use serde_json::{json, Value}; use std::collections::HashMap; #[derive(Deserialize, Debug)] struct TransactionValue { signatures: Vec<HashMap<String, Value>>, } #[derive(Deserialize, Debug)] struct Transaction { signatures: Vec<u8>, } fn main() { let value = json!({ "signatures": [{"0":1,"1":2,"2":3,"3":4,"4":5,"5":6,"6":7,"7":8,"8":9,"9":10,"10":11}] }); let tx_value = serde_json::from_value::<TransactionValue>(value).unwrap(); println!("{tx_value:#?}"); // 😱 This will be bug you because it won't sorted (randomly). let bug_u8s = tx_value .signatures .iter() .map(|e| { e.values() .map(|v| v.as_u64().unwrap() as u8) .collect::<Vec<u8>>() }) .collect::<Vec<_>>(); println!("randomly bug:{bug_u8s:#?}"); // assert_eq!(bug_u8s[0], vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,]); // 😱 This is sorted, but still bug because key is String. let still_bug_u8s = tx_value .signatures .iter() .map(|e| { let mut keys: Vec<&String> = e.keys().collect(); keys.sort(); keys.into_iter() .map(|k| e[k].as_u64().unwrap() as u8) .collect::<Vec<u8>>() }) .collect::<Vec<_>>(); println!("sorted string bug:{still_bug_u8s:#?}"); // assert_eq!(still_bug_u8s[0], vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,]); // 👍 This is sorted by usize. let u8s = tx_value .signatures .iter() .map(|e| { let mut keys = e .keys() .flat_map(|e| e.parse::<usize>()) .collect::<Vec<_>>(); keys.sort(); keys.into_iter() .map(|k| e[&k.to_string()].as_u64().unwrap() as u8) .collect::<Vec<u8>>() }) .collect::<Vec<_>>(); let tx = Transaction { signatures: u8s[0].clone(), }; println!("{tx:#?}"); assert_eq!(tx.signatures, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,]); }