Skip to content

Commit

Permalink
Let deserialize_bytes() handle an Array as well. (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
bhalleycf authored Mar 6, 2023
1 parent 118e424 commit 6d6776c
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ By default, Rust ⬄ JavaScript conversions in `serde-wasm-bindgen` follow this
| `HashMap<K, V>`, `BTreeMap`, etc. | `Map<K, V>` | any iterable over `[K, V]` |
| `Struct { key1: value1, … }` | `{ key1: value1, … }` object | |
| tuple, `Vec<T>`, `HashSet`, etc. | `T[]` array | any iterable over `T` |
| [`serde_bytes`] byte buffer | `Uint8Array` | `ArrayBuffer` |
| [`serde_bytes`] byte buffer | `Uint8Array` | `ArrayBuffer`, `Array` |

[as configured in Serde]: https://serde.rs/enum-representations.html
[safe integer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
Expand All @@ -121,13 +121,14 @@ are additionally supported when deserializing from JavaScript to the Rust type.

### Serializer configuration options

You can customize serialization from Rust to JavaScript by setting the following options on the [`Serializer::new()`](https://docs.rs/serde-wasm-bindgen/latest/serde_wasm_bindgen/struct.Serializer.html) instance:
You can customize serialization from Rust to JavaScript by setting the following options on the [`Serializer::new()`](https://docs.rs/serde-wasm-bindgen/latest/serde_wasm_bindgen/struct.Serializer.html) instance (all default to false):

- `.serialize_missing_as_null(true)`: Serialize `()`, unit structs and `Option::None` to `null` instead of `undefined`.
- `.serialize_maps_as_objects(true)`: Serialize maps into plain JavaScript objects instead of ES2015 Maps. false by default.
- `.serialize_maps_as_objects(true)`: Serialize maps into plain JavaScript objects instead of ES2015 Maps.
- `.serialize_large_number_types_as_bigints(true)`: Serialize `u64`, `i64`, `usize` and `isize` to `bigint`s instead of attempting to fit them into the [safe integer] `number` or failing.
- `.serialize_bytes_as_arrays(true)`: Serialize bytes into plain JavaScript arrays instead of ES2015 Uint8Arrays.

You can also use the `Serializer::json_compatible()` preset to create a JSON compatible serializer. It enables `serialize_missing_as_null` and `serialize_maps_as_objects` under the hood.
You can also use the `Serializer::json_compatible()` preset to create a JSON compatible serializer. It enables `serialize_missing_as_null`, `serialize_maps_as_objects`, and `serialize_bytes_as_arrays` under the hood.

## License

Expand Down
14 changes: 12 additions & 2 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ impl Deserializer {
_ => self.invalid_type(visitor),
}
}

fn deserialize_from_array<'de, V: de::Visitor<'de>>(
&self,
visitor: V,
array: &Array,
) -> Result<V::Value> {
visitor.visit_seq(SeqDeserializer::new(array.iter().map(Deserializer::from)))
}
}

impl<'de> de::Deserializer<'de> for Deserializer {
Expand Down Expand Up @@ -460,7 +468,7 @@ impl<'de> de::Deserializer<'de> for Deserializer {
/// - Any Rust sequence from Serde point of view ([`Vec`], [`HashSet`](std::collections::HashSet), etc.)
fn deserialize_seq<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
if let Some(arr) = self.value.dyn_ref::<Array>() {
visitor.visit_seq(SeqDeserializer::new(arr.iter().map(Deserializer::from)))
self.deserialize_from_array(visitor, arr)
} else if let Some(iter) = js_sys::try_iter(&self.value)? {
visitor.visit_seq(SeqAccess { iter })
} else {
Expand Down Expand Up @@ -559,10 +567,12 @@ impl<'de> de::Deserializer<'de> for Deserializer {
///
/// Supported inputs:
/// - `ArrayBuffer` - converted to an `Uint8Array` view first.
/// - `Uint8Array` - copied to a newly created `Vec<u8>` on the Rust side.
/// - `Uint8Array`, `Array` - copied to a newly created `Vec<u8>` on the Rust side.
fn deserialize_byte_buf<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
if let Some(bytes) = self.as_bytes() {
visitor.visit_byte_buf(bytes)
} else if let Some(arr) = self.value.dyn_ref::<Array>() {
self.deserialize_from_array(visitor, arr)
} else {
self.invalid_type(visitor)
}
Expand Down
19 changes: 16 additions & 3 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ pub struct Serializer {
serialize_missing_as_null: bool,
serialize_maps_as_objects: bool,
serialize_large_number_types_as_bigints: bool,
serialize_bytes_as_arrays: bool,
}

impl Serializer {
Expand All @@ -228,6 +229,7 @@ impl Serializer {
serialize_missing_as_null: false,
serialize_maps_as_objects: false,
serialize_large_number_types_as_bigints: false,
serialize_bytes_as_arrays: false,
}
}

Expand All @@ -240,6 +242,7 @@ impl Serializer {
serialize_missing_as_null: true,
serialize_maps_as_objects: true,
serialize_large_number_types_as_bigints: false,
serialize_bytes_as_arrays: true,
}
}

Expand All @@ -263,6 +266,13 @@ impl Serializer {
self.serialize_large_number_types_as_bigints = value;
self
}

/// Set to `true` to serialize bytes into plain JavaScript arrays instead of
/// ES2015 `Uint8Array`s. `false` by default.
pub const fn serialize_bytes_as_arrays(mut self, value: bool) -> Self {
self.serialize_bytes_as_arrays = value;
self
}
}

macro_rules! forward_to_into {
Expand Down Expand Up @@ -354,9 +364,12 @@ impl<'s> ser::Serializer for &'s Serializer {
//
// This is necessary because any allocation in WebAssembly can require reallocation of the
// backing memory, which will invalidate existing views (including `Uint8Array`).
Ok(JsValue::from(Uint8Array::new(
unsafe { Uint8Array::view(v) }.as_ref(),
)))
let view = unsafe { Uint8Array::view(v) };
if self.serialize_bytes_as_arrays {
Ok(JsValue::from(Array::from(view.as_ref())))
} else {
Ok(JsValue::from(Uint8Array::new(view.as_ref())))
}
}

fn serialize_none(self) -> Result {
Expand Down
14 changes: 14 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,20 @@ fn bytes() {
assert_eq!(deserialized.as_ref(), orig_src);
}

#[wasm_bindgen_test]
fn bytes_as_array() {
let src = [1, 2, 3];
// Convert to a JS value
let serializer = Serializer::new().serialize_bytes_as_arrays(true);
let bytes = &serde_bytes::Bytes::new(&src);
let value = bytes.serialize(&serializer).unwrap();
// Make sure the JS value is an Array.
value.dyn_ref::<js_sys::Array>().unwrap();
// Now, try to deserialize back.
let deserialized: serde_bytes::ByteBuf = from_value(value).unwrap();
assert_eq!(deserialized.as_ref(), src);
}

#[wasm_bindgen_test]
fn options() {
test_via_into(Some(0_u32), 0_u32);
Expand Down

0 comments on commit 6d6776c

Please sign in to comment.