Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate JsValue::from_serde and JsValue::into_serde #3031

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions examples/fetch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
36 changes: 2 additions & 34 deletions examples/fetch/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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)
}
3 changes: 2 additions & 1 deletion examples/raytrace-parallel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
5 changes: 2 additions & 3 deletions examples/raytrace-parallel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))?,
})
}
Expand Down
4 changes: 1 addition & 3 deletions examples/webxr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
19 changes: 9 additions & 10 deletions examples/webxr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -39,12 +36,15 @@ 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 {
Expand Down Expand Up @@ -77,7 +77,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);
Expand Down Expand Up @@ -121,7 +120,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!");
Expand Down
3 changes: 1 addition & 2 deletions guide/src/examples/fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
56 changes: 22 additions & 34 deletions guide/src/reference/arbitrary-data-with-serde.md
Original file line number Diff line number Diff line change
@@ -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 is 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
Expand All @@ -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`:
Expand All @@ -58,19 +56,19 @@ 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();
...
}
```
Expand All @@ -94,23 +92,13 @@ example.field2.push([5, 6]);
receive_example_from_js(example);
```

## An Alternative Approach: `serde-wasm-bindgen`

[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.

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.

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 &mdash; as always &mdash; make sure to profile your own use
cases as necessary.
## `JsValue::from_serde` / `JsValue::into_serde`

In previous versions of `wasm-bindgen`, `JsValue::from_serde` and
`JsValue::into_serde` were the recommended way of using Serde to convert
to/from JS values. These functions use `serde_json` to serialize types to JSON
and parse them on the other end. However, this caused problems when certain
features of `serde_json` and other crates were enabled that caused it to depend
on `wasm-bindgen`, creating a circular dependency, which is illegal in Rust and
caused people's code to fail to compile. So, they were deprecated in favor of
`serde-wasm-bindgen`.
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ 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`]
/// instead, or manually call `serde_json::to_string` + `JSON.parse`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// instead, or manually call `serde_json::to_string` + `JSON.parse`.
/// instead, or `gloo_utils::format::JsValueSerdeExt`, or manually call
/// `serde_json::to_string` + `JSON.parse`.

///
/// [dep-cycle-issue]: https://github.com/rustwasm/wasm-bindgen/issues/2770
/// [`serde-wasm-bindgen`]: https://docs.rs/serde-wasm-bindgen
///
/// 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
Expand All @@ -214,6 +221,7 @@ impl JsValue {
///
/// Returns any error encountered when serializing `T` into JSON.
#[cfg(feature = "serde-serialize")]
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` instead"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` instead"]
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` instead, or `gloo_utils::format::JsValueSerdeExt`"]

pub fn from_serde<T>(t: &T) -> serde_json::Result<JsValue>
where
T: serde::ser::Serialize + ?Sized,
Expand All @@ -225,6 +233,13 @@ 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`]
/// instead, or manually call `JSON.stringify` + `serde_json::from_str`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// instead, or manually call `JSON.stringify` + `serde_json::from_str`.
/// instead, or `gloo_utils::format::JsValueSerdeExt`, or manually call
/// `JSON.stringify` + `serde_json::from_str`.

///
/// [dep-cycle-issue]: https://github.com/rustwasm/wasm-bindgen/issues/2770
/// [`serde-wasm-bindgen`]: https://docs.rs/serde-wasm-bindgen
///
/// 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.
Expand All @@ -236,6 +251,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` instead"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` instead"]
#[deprecated = "causes dependency cycles, use `serde-wasm-bindgen` instead, or `gloo_utils::format::JsValueSerdeExt`"]

pub fn into_serde<T>(&self) -> serde_json::Result<T>
where
T: for<'a> serde::de::Deserialize<'a>,
Expand Down