0

With a friend of mine, we're trying to use the serde_json crate to deserialize some message sent by a WebSocket.

We are having a specific error, and we managed to recreate it with the following snippet of code:

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug)]
struct EffetSer {
    test: String
}

fn main() {
   let test_value = JsValue::from_str("{\"test\": \"value\"}");
   let test_value: EffetSer = test_value.into_serde().unwrap();
   log::error!("WOW : {:?}", test_value);
}

Our TOML has the following dependencies:

wasm-bindgen = { version = '0.2.63', features = ['serde-serialize'] }
serde = { version = '1.0', features = ["derive"] }
serde_json = '1.0.55'
js-sys = '0.3.40'

The error is the following:

app.js:310 panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"{\\\"test\\\": \\\"value\\\"}\", expected struct EffetSer", line: 1, column: 23)'

Any help would be very appreciated, as we're still struggling to understand what we're doing wrong and why we cannot deserialize our String.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
nugetchar
  • 172
  • 1
  • 3
  • 17
  • What is `Component`? I can't find it in your dependencies. It would help if you provide a minimal reproducible example. – Ibraheem Ahmed Nov 03 '20 at 19:40
  • You are absolutely right, I'm sorry I should have put a main. I'm editing my original post right away. – nugetchar Nov 03 '20 at 19:43
  • I believe the issue is with `JsValue`. Because using serde only works just fine: `let eff: EffetSer = serde_json::from_str("{\"test\": \"value\"}").unwrap();` – Ibraheem Ahmed Nov 03 '20 at 19:49

1 Answers1

0

The problem is likely misunderstanding of into_serde's semantics.

According to documentation, it works like this:

Invokes JSON.stringify on this value and then parses the resulting JSON into an arbitrary Rust value.

In other words, its semantics are as following:

  • convert each component of the JsValue to the corresponding serde internal element;
  • deserialize the required type from the given tree of components.

Now, what does this mean in our case? Well, you created JsValue using JsValue::from_str, which, again according to documentation,

Creates a new JS value which is a string.

So, the JsValue here is not an object, as you are likely assuming; it is a primitive - a string, which simply happens to have the shape of object's JSON representation. Then, when you invoke from_serde, Serde sees the string - not as input, but as internal representation, which cannot be transformed into object.


Now, what to do? There are several ways to fix this code:

  1. First and the most obvious: don't use JsValue at all, deserialize from &str directly with serde_json::from_str.
  2. Use js_sys::JSON::parse to get the object-like JsValue from string, and then convert it to the EffetSer with into_serde. This is likely to be less efficient, since it requires the round-trip of JSON::parse and JSON::serialize to convert the string to object and then back to string.
  3. Write your own method to convert JsValue to EffetSer directly. I'm not sure if this is possible, however, since I wasn't able to find a way to extract a single field from JS object.
Cerberus
  • 8,879
  • 1
  • 25
  • 40