mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-12 11:11:12 +03:00
Deprecate JsValue::from_serde
and JsValue::into_serde
(#3031)
* Deprecate `JsValue::from_serde` and `JsValue::into_serde` I've listed `serde-wasm-bindgen` as the replacement, and changed the section of the guide that talks about Serde to talk about `serde-wasm-bindgen` instead of the deprecated methods. I didn't remove it entirely because I can imagine someone remembering it and trying to look it back up, only to find that it no longer exists, which would quite frustrating. I also added a footnote about the deprecated methods in case someone remembers the old way and wants to know what happened. There were several examples using `from_serde`/`into_serde`, which I updated to use `serde-wasm-bindgen` or not use `serde` altogether. The `fetch` example was a bit weird, in that it took a JS value, parsed it into a Rust value, only to serialize it back into a JS value. I removed that entirely in favour of just passing the original JS value directly. I suppose it behaves slightly differently in that it loses the extra validation, but a panic isn't all that much better than a JS runtime error. * fmt * Mention JSON as an alternative to `serde-wasm-bindgen` * Use `gloo-utils` instead of raw `JSON` I was considering leaving the examples using `JSON` directly and mentioning `gloo-utils` as an aside, but that has the major footgun that `JSON.stringify(undefined) === undefined`, causing a panic when deserializing `undefined` since the return type of `JSON::stringify` isn't optional. `gloo-utils` works around this, so I recommended it instead. * Mention `gloo-utils` in API docs * Rephrase section about deprecated methods
This commit is contained in:
parent
b2f9e1247b
commit
823f57698c
@ -8,11 +8,9 @@ edition = "2018"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] }
|
||||
wasm-bindgen = "0.2.82"
|
||||
js-sys = "0.3.59"
|
||||
wasm-bindgen-futures = "0.4.32"
|
||||
serde = { version = "1.0.80", features = ["derive"] }
|
||||
serde_derive = "^1.0.59"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.4"
|
||||
|
@ -1,37 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
|
||||
/// A struct to hold some data from the github Branch API.
|
||||
///
|
||||
/// Note how we don't have to define every member -- serde will ignore extra
|
||||
/// data when deserializing
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Branch {
|
||||
pub name: String,
|
||||
pub commit: Commit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Commit {
|
||||
pub sha: String,
|
||||
pub commit: CommitDetails,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CommitDetails {
|
||||
pub author: Signature,
|
||||
pub committer: Signature,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Signature {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn run(repo: String) -> Result<JsValue, JsValue> {
|
||||
let mut opts = RequestInit::new();
|
||||
@ -56,9 +27,6 @@ pub async fn run(repo: String) -> Result<JsValue, JsValue> {
|
||||
// Convert this other `Promise` into a rust `Future`.
|
||||
let json = JsFuture::from(resp.json()?).await?;
|
||||
|
||||
// Use serde to parse the JSON into a struct.
|
||||
let branch_info: Branch = json.into_serde().unwrap();
|
||||
|
||||
// Send the `Branch` struct back to JS as an `Object`.
|
||||
Ok(JsValue::from_serde(&branch_info).unwrap())
|
||||
// Send the JSON response back to JS.
|
||||
Ok(json)
|
||||
}
|
||||
|
@ -13,8 +13,9 @@ js-sys = "0.3.59"
|
||||
rayon = "1.1.0"
|
||||
rayon-core = "1.5.0"
|
||||
raytracer = { git = 'https://github.com/alexcrichton/raytracer', branch = 'update-deps' }
|
||||
serde-wasm-bindgen = "0.4.3"
|
||||
futures-channel-preview = "0.3.0-alpha.18"
|
||||
wasm-bindgen = { version = "0.2.82", features = ['serde-serialize'] }
|
||||
wasm-bindgen = "0.2.82"
|
||||
wasm-bindgen-futures = "0.4.32"
|
||||
|
||||
[dependencies.web-sys]
|
||||
|
@ -28,11 +28,10 @@ impl Scene {
|
||||
/// Creates a new scene from the JSON description in `object`, which we
|
||||
/// deserialize here into an actual scene.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(object: &JsValue) -> Result<Scene, JsValue> {
|
||||
pub fn new(object: JsValue) -> Result<Scene, JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
Ok(Scene {
|
||||
inner: object
|
||||
.into_serde()
|
||||
inner: serde_wasm_bindgen::from_value(object)
|
||||
.map_err(|e| JsValue::from(e.to_string()))?,
|
||||
})
|
||||
}
|
||||
|
@ -10,10 +10,8 @@ crate-type = ["cdylib"]
|
||||
[dependencies]
|
||||
futures = "0.3.4"
|
||||
js-sys = "0.3.59"
|
||||
wasm-bindgen = {version = "0.2.82", features = ["serde-serialize"]}
|
||||
wasm-bindgen = "0.2.82"
|
||||
wasm-bindgen-futures = "0.4.32"
|
||||
serde = { version = "1.0.80", features = ["derive"] }
|
||||
serde_derive = "^1.0.59"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.36"
|
||||
|
@ -3,16 +3,13 @@
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
use futures::{future, Future};
|
||||
use js_sys::Promise;
|
||||
use js_sys::{Object, Promise, Reflect};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use utils::set_panic_hook;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::*;
|
||||
|
||||
// A macro to provide `println!(..)`-style syntax for `console.log` logging.
|
||||
@ -39,12 +36,16 @@ pub fn create_webgl_context(xr_mode: bool) -> Result<WebGl2RenderingContext, JsV
|
||||
.unwrap();
|
||||
|
||||
let gl: WebGl2RenderingContext = if xr_mode {
|
||||
let mut gl_attribs = HashMap::new();
|
||||
gl_attribs.insert(String::from("xrCompatible"), true);
|
||||
let js_gl_attribs = JsValue::from_serde(&gl_attribs).unwrap();
|
||||
let gl_attribs = Object::new();
|
||||
Reflect::set(
|
||||
&gl_attribs,
|
||||
&JsValue::from_str("xrCompatible"),
|
||||
&JsValue::TRUE,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
canvas
|
||||
.get_context_with_context_options("webgl2", &js_gl_attribs)?
|
||||
.get_context_with_context_options("webgl2", &gl_attribs)?
|
||||
.unwrap()
|
||||
.dyn_into()?
|
||||
} else {
|
||||
@ -77,7 +78,6 @@ impl XrApp {
|
||||
pub fn init(&self) -> Promise {
|
||||
log!("Starting WebXR...");
|
||||
let navigator: web_sys::Navigator = web_sys::window().unwrap().navigator();
|
||||
let gpu = navigator.gpu();
|
||||
let xr = navigator.xr();
|
||||
let session_mode = XrSessionMode::Inline;
|
||||
let session_supported_promise = xr.is_session_supported(session_mode);
|
||||
@ -121,7 +121,7 @@ impl XrApp {
|
||||
let g = f.clone();
|
||||
|
||||
let mut i = 0;
|
||||
*g.borrow_mut() = Some(Closure::new(move |time: f64, frame: XrFrame| {
|
||||
*g.borrow_mut() = Some(Closure::new(move |_time: f64, frame: XrFrame| {
|
||||
log!("Frame rendering...");
|
||||
if i > 2 {
|
||||
log!("All done!");
|
||||
|
@ -11,8 +11,7 @@ then parses the resulting JSON.
|
||||
## `Cargo.toml`
|
||||
|
||||
The `Cargo.toml` enables a number of features related to the `fetch` API and
|
||||
types used: `Headers`, `Request`, etc. It also enables `wasm-bindgen`'s `serde`
|
||||
support.
|
||||
types used: `Headers`, `Request`, etc.
|
||||
|
||||
```toml
|
||||
{{#include ../../../examples/fetch/Cargo.toml}}
|
||||
|
@ -1,21 +1,19 @@
|
||||
# Serializing and Deserializing Arbitrary Data Into and From `JsValue` with Serde
|
||||
|
||||
It's possible to pass arbitrary data from Rust to JavaScript by serializing it
|
||||
to JSON with [Serde](https://github.com/serde-rs/serde). `wasm-bindgen` includes
|
||||
the `JsValue` type, which streamlines serializing and deserializing.
|
||||
with [Serde](https://github.com/serde-rs/serde). This can be done through the
|
||||
[`serde-wasm-bindgen`](https://docs.rs/serde-wasm-bindgen) crate.
|
||||
|
||||
## Enable the `"serde-serialize"` Feature
|
||||
## Add dependencies
|
||||
|
||||
To enable the `"serde-serialize"` feature, do two things in `Cargo.toml`:
|
||||
|
||||
1. Add the `serde` and `serde_derive` crates to `[dependencies]`.
|
||||
2. Add `features = ["serde-serialize"]` to the existing `wasm-bindgen`
|
||||
dependency.
|
||||
To use `serde-wasm-bindgen`, you first have to add it as a dependency in your
|
||||
`Cargo.toml`. You also need the `serde` crate, with the `derive` feature
|
||||
enabled, to allow your types to be serialized and deserialized with Serde.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
```
|
||||
|
||||
## Derive the `Serialize` and `Deserialize` Traits
|
||||
@ -42,7 +40,7 @@ pub struct Example {
|
||||
}
|
||||
```
|
||||
|
||||
## Send it to JavaScript with `JsValue::from_serde`
|
||||
## Send it to JavaScript with `serde_wasm_bindgen::to_value`
|
||||
|
||||
Here's a function that will pass an `Example` to JavaScript by serializing it to
|
||||
`JsValue`:
|
||||
@ -58,28 +56,28 @@ pub fn send_example_to_js() -> JsValue {
|
||||
field3: [1., 2., 3., 4.]
|
||||
};
|
||||
|
||||
JsValue::from_serde(&example).unwrap()
|
||||
serde_wasm_bindgen::to_value(&example).unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
## Receive it from JavaScript with `JsValue::into_serde`
|
||||
## Receive it from JavaScript with `serde_wasm_bindgen::from_value`
|
||||
|
||||
Here's a function that will receive a `JsValue` parameter from JavaScript and
|
||||
then deserialize an `Example` from it:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn receive_example_from_js(val: &JsValue) {
|
||||
let example: Example = val.into_serde().unwrap();
|
||||
pub fn receive_example_from_js(val: JsValue) {
|
||||
let example: Example = serde_wasm_bindgen::from_value(val).unwrap();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript Usage
|
||||
|
||||
In the `JsValue` that JavaScript gets, `field1` will be an `Object` (not a
|
||||
JavaScript `Map`), `field2` will be a JavaScript `Array` whose members are
|
||||
`Array`s of numbers, and `field3` will be an `Array` of numbers.
|
||||
In the `JsValue` that JavaScript gets, `field1` will be a `Map`, `field2` will
|
||||
be a JavaScript `Array` whose members are `Array`s of numbers, and `field3`
|
||||
will be an `Array` of numbers.
|
||||
|
||||
```js
|
||||
import { send_example_to_js, receive_example_from_js } from "example";
|
||||
@ -94,23 +92,61 @@ example.field2.push([5, 6]);
|
||||
receive_example_from_js(example);
|
||||
```
|
||||
|
||||
## An Alternative Approach: `serde-wasm-bindgen`
|
||||
## An alternative approach - using JSON
|
||||
|
||||
[The `serde-wasm-bindgen`
|
||||
crate](https://github.com/cloudflare/serde-wasm-bindgen) serializes and
|
||||
deserializes Rust structures directly to `JsValue`s, without going through
|
||||
temporary JSON stringification. This approach has both advantages and
|
||||
disadvantages.
|
||||
`serde-wasm-bindgen` works by directly manipulating JavaScript values. This
|
||||
requires a lot of calls back and forth between Rust and JavaScript, which can
|
||||
sometimes be slow. An alternative way of doing this is to serialize values to
|
||||
JSON, and then parse them on the other end. Browsers' JSON implementations are
|
||||
usually quite fast, and so this approach can outstrip `serde-wasm-bindgen`'s
|
||||
performance in some cases.
|
||||
|
||||
The primary advantage is smaller code size: going through JSON entrenches code
|
||||
to stringify and parse floating point numbers, which is not a small amount of
|
||||
code. It also supports more types than JSON does, such as `Map`, `Set`, and
|
||||
array buffers.
|
||||
That's not to say that using JSON is always faster, though - the JSON approach
|
||||
can be anywhere from 2x to 0.2x the speed of `serde-wasm-bindgen`, depending on
|
||||
the JS runtime and the values being passed. It also leads to larger code size
|
||||
than `serde-wasm-bindgen`. So, make sure to profile each for your own use
|
||||
cases.
|
||||
|
||||
There are two primary disadvantages. The first is that it is not always
|
||||
compatible with the default JSON-based serialization. The second is that it
|
||||
performs more calls back and forth between JS and Wasm, which has not been fully
|
||||
optimized in all engines, meaning it can sometimes be a speed
|
||||
regression. However, in other cases, it is a speed up over the JSON-based
|
||||
stringification, so — as always — make sure to profile your own use
|
||||
cases as necessary.
|
||||
This approach is implemented in [`gloo_utils::format::JsValueSerdeExt`]:
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
gloo-utils = { version = "0.1", features = ["serde"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn send_example_to_js() -> JsValue {
|
||||
let mut field1 = HashMap::new();
|
||||
field1.insert(0, String::from("ex"));
|
||||
let example = Example {
|
||||
field1,
|
||||
field2: vec![vec![1., 2.], vec![3., 4.]],
|
||||
field3: [1., 2., 3., 4.]
|
||||
};
|
||||
|
||||
JsValue::from_serde(&example).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn receive_example_from_js(val: JsValue) {
|
||||
let example: Example = val.into_serde().unwrap();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[`gloo_utils::format::JsValueSerdeExt`]: https://docs.rs/gloo-utils/latest/gloo_utils/format/trait.JsValueSerdeExt.html
|
||||
|
||||
## History
|
||||
|
||||
In previous versions of `wasm-bindgen`, `gloo-utils`'s JSON-based Serde support
|
||||
(`JsValue::from_serde` and `JsValue::into_serde`) was built into `wasm-bindgen`
|
||||
itself. However, this required a dependency on `serde_json`, which had a
|
||||
problem: with certain features of `serde_json` and other crates enabled,
|
||||
`serde_json` would end up with a circular dependency on `wasm-bindgen`, which
|
||||
is illegal in Rust and caused people's code to fail to compile. So, these
|
||||
methods were extracted out into `gloo-utils` with an extension trait and the
|
||||
originals were deprecated.
|
||||
|
18
src/lib.rs
18
src/lib.rs
@ -201,6 +201,14 @@ impl JsValue {
|
||||
/// Creates a new `JsValue` from the JSON serialization of the object `t`
|
||||
/// provided.
|
||||
///
|
||||
/// **This function is deprecated**, due to [creating a dependency cycle in
|
||||
/// some circumstances][dep-cycle-issue]. Use [`serde-wasm-bindgen`] or
|
||||
/// [`gloo_utils::format::JsValueSerdeExt`] instead.
|
||||
///
|
||||
/// [dep-cycle-issue]: https://github.com/rustwasm/wasm-bindgen/issues/2770
|
||||
/// [`serde-wasm-bindgen`]: https://docs.rs/serde-wasm-bindgen
|
||||
/// [`gloo_utils::format::JsValueSerdeExt`]: https://docs.rs/gloo-utils/latest/gloo_utils/format/trait.JsValueSerdeExt.html
|
||||
///
|
||||
/// This function will serialize the provided value `t` to a JSON string,
|
||||
/// send the JSON string to JS, parse it into a JS object, and then return
|
||||
/// a handle to the JS object. This is unlikely to be super speedy so it's
|
||||
@ -214,6 +222,7 @@ impl JsValue {
|
||||
///
|
||||
/// Returns any error encountered when serializing `T` into JSON.
|
||||
#[cfg(feature = "serde-serialize")]
|
||||
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` or `gloo_utils::format::JsValueSerdeExt` instead"]
|
||||
pub fn from_serde<T>(t: &T) -> serde_json::Result<JsValue>
|
||||
where
|
||||
T: serde::ser::Serialize + ?Sized,
|
||||
@ -225,6 +234,14 @@ impl JsValue {
|
||||
/// Invokes `JSON.stringify` on this value and then parses the resulting
|
||||
/// JSON into an arbitrary Rust value.
|
||||
///
|
||||
/// **This function is deprecated**, due to [creating a dependency cycle in
|
||||
/// some circumstances][dep-cycle-issue]. Use [`serde-wasm-bindgen`] or
|
||||
/// [`gloo_utils::format::JsValueSerdeExt`] instead.
|
||||
///
|
||||
/// [dep-cycle-issue]: https://github.com/rustwasm/wasm-bindgen/issues/2770
|
||||
/// [`serde-wasm-bindgen`]: https://docs.rs/serde-wasm-bindgen
|
||||
/// [`gloo_utils::format::JsValueSerdeExt`]: https://docs.rs/gloo-utils/latest/gloo_utils/format/trait.JsValueSerdeExt.html
|
||||
///
|
||||
/// This function will first call `JSON.stringify` on the `JsValue` itself.
|
||||
/// The resulting string is then passed into Rust which then parses it as
|
||||
/// JSON into the resulting value.
|
||||
@ -236,6 +253,7 @@ impl JsValue {
|
||||
///
|
||||
/// Returns any error encountered when parsing the JSON into a `T`.
|
||||
#[cfg(feature = "serde-serialize")]
|
||||
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` or `gloo_utils::format::JsValueSerdeExt` instead"]
|
||||
pub fn into_serde<T>(&self) -> serde_json::Result<T>
|
||||
where
|
||||
T: for<'a> serde::de::Deserialize<'a>,
|
||||
|
Loading…
Reference in New Issue
Block a user