Repository: graphql-rust/juniper (language: rust)

Issue:
juniper_graphql_ws: serde deserialization error with null values
**Describe the bug**
When a client start a subscription it sends a payload which can contain optional variables. Currently, serde `default` field attribute [is being used](https://github.com/graphql-rust/juniper/blob/master/juniper_graphql_ws/src/client_message.rs#L13).
It works if the key is not present or if it is an empty object (`{}`) but yield a serde error if it's `null` (`invalid type: null, expected a map`).

**To Reproduce**
* Launch the `actix_subscriptions` example.
* Launch a client (graphiql for example) and send a payload with `"variables": null` (with graphiql it should send `null` if no variable are specified)


**Expected behavior**
Treating the field as if it were absent.


**Additional context**
This kind of problem was already discussed on [this issue](https://github.com/serde-rs/serde/issues/1098).

**Potential fix**
One way to solve the issue is to use the `default` attribute and `deserialize_with` for example:
```rust
fn unwrap_or_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de> + Default
{
    Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(bound(deserialize = "S: ScalarValue"))]
#[serde(rename_all = "camelCase")]
pub struct StartPayload<S: ScalarValue> {
    pub query: String,
    #[serde(default, deserialize_with = "unwrap_or_default")]
    pub variables: Variables<S>,
    pub operation_name: Option<String>,
}
```



Discussion / hints:
Sounds great, thank you for the report and the suggested fix! Would you be willing to put a PR up?
No problem. I'll open a PR.

Fix patch:
diff --git a/juniper_graphql_ws/CHANGELOG.md b/juniper_graphql_ws/CHANGELOG.md
index 052324725..51a7a654c 100644
--- a/juniper_graphql_ws/CHANGELOG.md
+++ b/juniper_graphql_ws/CHANGELOG.md
@@ -1,3 +1,4 @@
 # master
 
+- Fix null deserialization issue ([#735](https://github.com/graphql-rust/juniper/issues/735))
 - Initial Release
diff --git a/juniper_graphql_ws/src/client_message.rs b/juniper_graphql_ws/src/client_message.rs
index 1e20caef1..8d8268192 100644
--- a/juniper_graphql_ws/src/client_message.rs
+++ b/juniper_graphql_ws/src/client_message.rs
@@ -1,3 +1,4 @@
+use crate::utils::default_for_null;
 use juniper::{ScalarValue, Variables};
 
 /// The payload for a client's "start" message. This triggers execution of a query, mutation, or
@@ -10,7 +11,7 @@ pub struct StartPayload<S: ScalarValue> {
     pub query: String,
 
     /// The optional variables.
-    #[serde(default)]
+    #[serde(default, deserialize_with = "default_for_null")]
     pub variables: Variables<S>,
 
     /// The optional operation name (required if the document contains multiple operations).
@@ -27,7 +28,7 @@ pub enum ClientMessage<S: ScalarValue> {
     ConnectionInit {
         /// Optional parameters of any type sent from the client. These are often used for
         /// authentication.
-        #[serde(default)]
+        #[serde(default, deserialize_with = "default_for_null")]
         payload: Variables<S>,
     },
     /// Start messages are used to execute a GraphQL operation.
@@ -128,4 +129,20 @@ mod test {
             serde_json::from_str(r##"{"type": "connection_terminate"}"##).unwrap(),
         );
     }
+
+    #[test]
+    fn test_deserialization_of_null() -> serde_json::Result<()> {
+        let payload = r#"{"query":"query","variables":null}"#;
+        let payload: StartPayload<DefaultScalarValue> = serde_json::from_str(payload)?;
+
+        let expected = StartPayload {
+            query: "query".into(),
+            variables: Variables::default(),
+            operation_name: None,
+        };
+
+        assert_eq!(expected, payload);
+
+        Ok(())
+    }
 }
diff --git a/juniper_graphql_ws/src/lib.rs b/juniper_graphql_ws/src/lib.rs
index 7cf73eecb..fbbb7565e 100644
--- a/juniper_graphql_ws/src/lib.rs
+++ b/juniper_graphql_ws/src/lib.rs
@@ -21,6 +21,8 @@ pub use server_message::*;
 mod schema;
 pub use schema::*;
 
+mod utils;
+
 use juniper::{
     futures::{
         channel::oneshot,
diff --git a/juniper_graphql_ws/src/utils.rs b/juniper_graphql_ws/src/utils.rs
new file mode 100644
index 000000000..75106a4cf
--- /dev/null
+++ b/juniper_graphql_ws/src/utils.rs
@@ -0,0 +1,9 @@
+use serde::{Deserialize, Deserializer};
+
+pub(crate) fn default_for_null<'de, D, T>(deserializer: D) -> Result<T, D::Error>
+where
+    D: Deserializer<'de>,
+    T: Deserialize<'de> + Default,
+{
+    Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
+}


Test patch: