Bug 1396821 - Switch from rustc_serialize to serde_json. draft
authorHenrik Skupin <mail@hskupin.info>
Sat, 03 Mar 2018 23:17:36 +0100
changeset 770246 7f43e89dd2151ce1ffb6de599c2bf9d9a49f7fa0
parent 770228 025426a3e2e780d33bb666c78bfbaf22b3456c19
child 770247 5cafb8f1a48071257a5514c33730b5cdc7f43f97
child 770829 1afd8bca10268c17df935613ff2f02df993a011a
push id103364
push userbmo:hskupin@gmail.com
push dateTue, 20 Mar 2018 22:36:47 +0000
bugs1396821
milestone61.0a1
Bug 1396821 - Switch from rustc_serialize to serde_json. Instead use serde. This is the simplest possible conversion using the serde Value type everywhere. The intent is to use the automatically derived deserializers in the future. MozReview-Commit-ID: 9UIr9gialm5
Cargo.lock
testing/webdriver/Cargo.toml
testing/webdriver/src/actions.rs
testing/webdriver/src/capabilities.rs
testing/webdriver/src/command.rs
testing/webdriver/src/common.rs
testing/webdriver/src/error.rs
testing/webdriver/src/httpapi.rs
testing/webdriver/src/lib.rs
testing/webdriver/src/response.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -98,16 +98,24 @@ dependencies = [
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "base64"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "base64"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1568,16 +1576,27 @@ name = "serde_derive_internals"
 version = "0.19.0"
 source = "git+https://github.com/gankro/serde?branch=deserialize_from_enums4#93e24f268ab99c0df10e2183587284e02ca30e9e"
 dependencies = [
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "synom 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "serde_json"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "servo_arc"
 version = "0.1.1"
 dependencies = [
  "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -2006,21 +2025,24 @@ dependencies = [
  "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webdriver"
 version = "0.35.0"
 dependencies = [
+ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.27 (git+https://github.com/gankro/serde?branch=deserialize_from_enums4)",
+ "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.57.0"
@@ -2189,16 +2211,17 @@ dependencies = [
 [metadata]
 "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"
 "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
 "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
 "checksum app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"
 "checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
 "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
 "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
+"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
 "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
 "checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
 "checksum bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3fb369af639822830328794eba2501b3479652fcd021b2aeb1ed4984202afd"
 "checksum bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)" = "603ed8d8392ace9581e834e26bd09799bf1e989a79bd1aedbb893e72962bdc6e"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
 "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
 "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
 "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75"
@@ -2334,16 +2357,17 @@ dependencies = [
 "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
 "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
 "checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"
 "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
 "checksum serde_derive 1.0.27 (git+https://github.com/gankro/serde?branch=deserialize_from_enums4)" = "<none>"
 "checksum serde_derive_internals 0.19.0 (git+https://github.com/gankro/serde?branch=deserialize_from_enums4)" = "<none>"
+"checksum serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57781ed845b8e742fc2bf306aba8e3b408fe8c366b900e3769fbc39f49eb8b39"
 "checksum simd 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd0805c7363ab51a829a1511ad24b6ed0349feaa756c4bc2f977f9f496e6673"
 "checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
 "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 "checksum smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79b776f00dfe01df905fa3b2eaa1659522e99e3fc4a7b1334171622205c4bdcf"
 "checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
 "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
 "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
 "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
--- a/testing/webdriver/Cargo.toml
+++ b/testing/webdriver/Cargo.toml
@@ -5,16 +5,19 @@ authors = ["Mozilla"]
 description = "Library implementing the wire protocol for the W3C WebDriver specification."
 keywords = ["webdriver", "browser", "automation", "protocol", "w3c"]
 documentation = "https://docs.rs/webdriver"
 repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/webdriver"
 readme = "README.md"
 license = "MPL-2.0"
 
 [dependencies]
+base64 = "0.5"
 cookie = { version = "0.10", default-features = false }
 hyper = "0.10"
 log = "0.4"
 regex = "0.2"
-rustc-serialize = "0.3"
+serde = "1.0"
+serde_json = "1.0"
+serde_derive = "1.0"
 time = "0.1"
 unicode-segmentation = "1.1.0"
 url = "1"
--- a/testing/webdriver/src/actions.rs
+++ b/testing/webdriver/src/actions.rs
@@ -1,660 +1,445 @@
-use command::Parameters;
-use common::{Nullable, WebElement};
-use error::{WebDriverResult, WebDriverError, ErrorStatus};
-use rustc_serialize::json::{ToJson, Json};
-use unicode_segmentation::UnicodeSegmentation;
-use std::collections::BTreeMap;
+use common::WebElement;
+use serde::{Deserialize, Deserializer};
+use serde_json::{Value, Map};
 use std::default::Default;
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize)]
 pub struct ActionSequence {
-    pub id: Nullable<String>,
+    pub id: Option<String>,
     pub actions: ActionsType
 }
 
-impl Parameters for ActionSequence {
-    fn from_json(body: &Json) -> WebDriverResult<ActionSequence> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Actions chain was not an object");
-
-        let type_name = try_opt!(try_opt!(data.get("type"),
-                                          ErrorStatus::InvalidArgument,
-                                          "Missing type parameter").as_string(),
-                                 ErrorStatus::InvalidArgument,
-                                 "Parameter ;type' was not a string");
-
-        let id = match data.get("id") {
-            Some(x) => Some(try_opt!(x.as_string(),
-                                     ErrorStatus::InvalidArgument,
-                                     "Parameter 'id' was not a string").to_owned()),
-            None => None
-        };
-
-
-        // Note that unlike the spec we get the pointer parameters in ActionsType::from_json
+impl<'de> Deserialize<'de> for ActionSequence {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where D: Deserializer<'de>
+    {
+        #[derive(Deserialize)]
+        #[serde(tag = "type", rename_all = "lowercase")]
+        enum Helper {
+            Null {
+                id: Option<String>,
+                actions: Vec<NullActionItem>,
+            },
+            Key {
+                id: Option<String>,
+                actions: Vec<KeyActionItem>,
+            },
+            Pointer {
+                id: Option<String>,
+                #[serde(default)]
+                parameters: PointerActionParameters,
+                actions: Vec<PointerActionItem>,
+            },
+        }
 
-        let actions = match type_name {
-            "none" | "key" | "pointer" => try!(ActionsType::from_json(&body)),
-            _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                "Invalid action type"))
-        };
-
-        Ok(ActionSequence {
-            id: id.into(),
-            actions: actions
-        })
-    }
-}
-
-impl ToJson for ActionSequence {
-    fn to_json(&self) -> Json {
-        let mut data: BTreeMap<String, Json> = BTreeMap::new();
-        data.insert("id".into(), self.id.to_json());
-        let (action_type, actions) = match self.actions {
-            ActionsType::Null(ref actions) => {
-                ("none",
-                 actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
-            }
-            ActionsType::Key(ref actions) => {
-                ("key",
-                 actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
-            }
-            ActionsType::Pointer(ref parameters, ref actions) => {
-                data.insert("parameters".into(), parameters.to_json());
-                ("pointer",
-                 actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
+        match Helper::deserialize(deserializer)? {
+            Helper::Null { id, actions } => {
+                Ok(ActionSequence {
+                    id: id,
+                    actions: ActionsType::Null{actions},
+                })
             }
-        };
-        data.insert("type".into(), action_type.to_json());
-        data.insert("actions".into(), actions.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum ActionsType {
-    Null(Vec<NullActionItem>),
-    Key(Vec<KeyActionItem>),
-    Pointer(PointerActionParameters, Vec<PointerActionItem>)
-}
-
-impl Parameters for ActionsType {
-    fn from_json(body: &Json) -> WebDriverResult<ActionsType> {
-        // These unwraps are OK as long as this is only called from ActionSequence::from_json
-        let data = body.as_object().expect("Body should be a JSON Object");
-        let actions_type = body.find("type").and_then(|x| x.as_string()).expect("Type should be a string");
-        let actions_chain = try_opt!(try_opt!(data.get("actions"),
-                                              ErrorStatus::InvalidArgument,
-                                              "Missing actions parameter").as_array(),
-                                     ErrorStatus::InvalidArgument,
-                                     "Parameter 'actions' was not an array");
-        match actions_type {
-            "none" => {
-                let mut actions = Vec::with_capacity(actions_chain.len());
-                for action_body in actions_chain.iter() {
-                    actions.push(try!(NullActionItem::from_json(action_body)));
-                };
-                Ok(ActionsType::Null(actions))
-            },
-            "key" => {
-                let mut actions = Vec::with_capacity(actions_chain.len());
-                for action_body in actions_chain.iter() {
-                    actions.push(try!(KeyActionItem::from_json(action_body)));
-                };
-                Ok(ActionsType::Key(actions))
-            },
-            "pointer" => {
-                let mut actions = Vec::with_capacity(actions_chain.len());
-                let parameters = match data.get("parameters") {
-                    Some(x) => try!(PointerActionParameters::from_json(x)),
-                    None => Default::default()
-                };
-
-                for action_body in actions_chain.iter() {
-                    actions.push(try!(PointerActionItem::from_json(action_body)));
-                }
-                Ok(ActionsType::Pointer(parameters, actions))
+            Helper::Key { id, actions } => {
+                Ok(ActionSequence {
+                    id: id,
+                    actions: ActionsType::Key{actions},
+                })
             }
-            _ => panic!("Got unexpected action type after checking type")
+            Helper::Pointer { id, parameters, actions } => {
+                Ok(ActionSequence {
+                    id: id,
+                    actions: ActionsType::Pointer{parameters, actions},
+                })
+            }
         }
     }
 }
 
-#[derive(Debug, PartialEq)]
+impl<'a> From<&'a ActionSequence> for Value {
+    fn from(params: &'a ActionSequence) -> Value {
+        let mut data: Map<String, Value> = Map::new();
+        data.insert("id".into(), params.id.clone().map(|x| x.into()).unwrap_or(Value::Null));
+        let (action_type, actions) = match params.actions {
+            ActionsType::Null {ref actions} => {
+                ("none",
+                 actions.iter().map(|x| x.into()).collect::<Vec<Value>>())
+            }
+            ActionsType::Key {ref actions} => {
+                ("key",
+                 actions.iter().map(|x| x.into()).collect::<Vec<Value>>())
+            }
+            ActionsType::Pointer {ref parameters, ref actions} => {
+                data.insert("parameters".into(), parameters.into());
+                ("pointer",
+                 actions.iter().map(|x| x.into()).collect::<Vec<Value>>())
+            }
+        };
+        data.insert("type".into(), action_type.into());
+        data.insert("actions".into(), actions.into());
+        Value::Object(data)
+    }
+}
+
+#[derive(Debug, PartialEq, Serialize)]
+pub enum ActionsType {
+    Null {actions: Vec<NullActionItem>},
+    Key {actions: Vec<KeyActionItem>},
+    Pointer {parameters: PointerActionParameters, actions:Vec<PointerActionItem>}
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all="lowercase")]
 pub enum PointerType {
     Mouse,
     Pen,
     Touch,
 }
 
-impl Parameters for PointerType {
-    fn from_json(body: &Json) -> WebDriverResult<PointerType> {
-        match body.as_string() {
-            Some("mouse") => Ok(PointerType::Mouse),
-            Some("pen") => Ok(PointerType::Pen),
-            Some("touch") => Ok(PointerType::Touch),
-            Some(_) => Err(WebDriverError::new(
-                ErrorStatus::InvalidArgument,
-                "Unsupported pointer type"
-            )),
-            None => Err(WebDriverError::new(
-                ErrorStatus::InvalidArgument,
-                "Pointer type was not a string"
-            ))
+impl<'a> From<&'a PointerType> for Value {
+    fn from(params: &'a PointerType) -> Value {
+        match *params {
+            PointerType::Mouse => "mouse".into(),
+            PointerType::Pen => "pen".into(),
+            PointerType::Touch => "touch".into(),
         }
     }
 }
 
-impl ToJson for PointerType {
-    fn to_json(&self) -> Json {
-        match *self {
-            PointerType::Mouse => "mouse".to_json(),
-            PointerType::Pen => "pen".to_json(),
-            PointerType::Touch => "touch".to_json(),
-        }.to_json()
-    }
-}
-
 impl Default for PointerType {
     fn default() -> PointerType {
         PointerType::Mouse
     }
 }
 
-#[derive(Debug, Default, PartialEq)]
+#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
 pub struct PointerActionParameters {
+    #[serde(rename="pointerType")]
     pub pointer_type: PointerType
 }
 
-impl Parameters for PointerActionParameters {
-    fn from_json(body: &Json) -> WebDriverResult<PointerActionParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Parameter 'parameters' was not an object");
-        let pointer_type = match data.get("pointerType") {
-            Some(x) => try!(PointerType::from_json(x)),
-            None => PointerType::default()
-        };
-        Ok(PointerActionParameters {
-            pointer_type: pointer_type
-        })
+impl<'a> From<&'a PointerActionParameters> for Value {
+    fn from(params: &'a PointerActionParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("pointerType".to_owned(),
+                    (&params.pointer_type).into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for PointerActionParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("pointerType".to_owned(),
-                    self.pointer_type.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
 pub enum NullActionItem {
     General(GeneralAction)
 }
 
-impl Parameters for NullActionItem {
-    fn from_json(body: &Json) -> WebDriverResult<NullActionItem> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Actions chain was not an object");
-        let type_name = try_opt!(
-            try_opt!(data.get("type"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'type' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'type' was not a string");
-        match type_name {
-            "pause" => Ok(NullActionItem::General(
-                try!(GeneralAction::from_json(body)))),
-            _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                "Invalid type attribute"))
+impl<'a> From<&'a NullActionItem> for Value {
+    fn from(params: &'a NullActionItem) -> Value {
+        match *params {
+            NullActionItem::General(ref x) => x.into(),
         }
     }
 }
 
-impl ToJson for NullActionItem {
-    fn to_json(&self) -> Json {
-        match self {
-            &NullActionItem::General(ref x) => x.to_json(),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
 pub enum KeyActionItem {
     General(GeneralAction),
     Key(KeyAction)
 }
 
-impl Parameters for KeyActionItem {
-    fn from_json(body: &Json) -> WebDriverResult<KeyActionItem> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Key action item was not an object");
-        let type_name = try_opt!(
-            try_opt!(data.get("type"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'type' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'type' was not a string");
-        match type_name {
-            "pause" => Ok(KeyActionItem::General(
-                try!(GeneralAction::from_json(body)))),
-            _ => Ok(KeyActionItem::Key(
-                try!(KeyAction::from_json(body))))
+impl<'a> From<&'a KeyActionItem> for Value {
+    fn from(params: &'a KeyActionItem) -> Value {
+        match *params {
+            KeyActionItem::General(ref x) => x.into(),
+            KeyActionItem::Key(ref x) => x.into()
         }
     }
 }
 
-impl ToJson for KeyActionItem {
-    fn to_json(&self) -> Json {
-        match *self {
-            KeyActionItem::General(ref x) => x.to_json(),
-            KeyActionItem::Key(ref x) => x.to_json()
-        }
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
 pub enum PointerActionItem {
     General(GeneralAction),
     Pointer(PointerAction)
 }
 
-impl Parameters for PointerActionItem {
-    fn from_json(body: &Json) -> WebDriverResult<PointerActionItem> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Pointer action item was not an object");
-        let type_name = try_opt!(
-            try_opt!(data.get("type"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'type' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'type' was not a string");
-
-        match type_name {
-            "pause" => Ok(PointerActionItem::General(try!(GeneralAction::from_json(body)))),
-            _ => Ok(PointerActionItem::Pointer(try!(PointerAction::from_json(body))))
+impl<'a> From<&'a PointerActionItem> for Value {
+    fn from(params: &'a PointerActionItem) -> Value {
+        match *params {
+            PointerActionItem::General(ref x) => x.into(),
+            PointerActionItem::Pointer(ref x) => x.into()
         }
     }
 }
 
-impl ToJson for PointerActionItem {
-    fn to_json(&self) -> Json {
-        match self {
-            &PointerActionItem::General(ref x) => x.to_json(),
-            &PointerActionItem::Pointer(ref x) => x.to_json()
-        }
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
 pub enum GeneralAction {
     Pause(PauseAction)
 }
 
-impl Parameters for GeneralAction {
-    fn from_json(body: &Json) -> WebDriverResult<GeneralAction> {
-        match body.find("type").and_then(|x| x.as_string()) {
-            Some("pause") => Ok(GeneralAction::Pause(try!(PauseAction::from_json(body)))),
-            _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                         "Invalid or missing type attribute"))
+impl<'a> From<&'a GeneralAction> for Value {
+    fn from(params: &'a GeneralAction) -> Value {
+        match *params {
+            GeneralAction::Pause(ref x) => x.into()
         }
     }
 }
 
-impl ToJson for GeneralAction {
-    fn to_json(&self) -> Json {
-        match self {
-            &GeneralAction::Pause(ref x) => x.to_json()
-        }
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct PauseAction {
     pub duration: u64
 }
 
-impl Parameters for PauseAction {
-    fn from_json(body: &Json) -> WebDriverResult<PauseAction> {
-        let default = Json::U64(0);
-        Ok(PauseAction {
-            duration: try_opt!(body.find("duration").unwrap_or(&default).as_u64(),
-                               ErrorStatus::InvalidArgument,
-                               "Parameter 'duration' was not a positive integer")
-        })
-    }
-}
-
-impl ToJson for PauseAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
+impl<'a> From<&'a PauseAction> for Value {
+    fn from(params: &'a PauseAction) -> Value {
+        let mut data = Map::new();
         data.insert("type".to_owned(),
-                    "pause".to_json());
+                    "pause".into());
         data.insert("duration".to_owned(),
-                    self.duration.to_json());
-        Json::Object(data)
+                    params.duration.into());
+        Value::Object(data)
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
 pub enum KeyAction {
+    #[serde(rename="keyUp")]
     Up(KeyUpAction),
+    #[serde(rename="keyDown")]
     Down(KeyDownAction)
 }
 
-impl Parameters for KeyAction {
-    fn from_json(body: &Json) -> WebDriverResult<KeyAction> {
-        match body.find("type").and_then(|x| x.as_string()) {
-            Some("keyDown") => Ok(KeyAction::Down(try!(KeyDownAction::from_json(body)))),
-            Some("keyUp") => Ok(KeyAction::Up(try!(KeyUpAction::from_json(body)))),
-            Some(_) | None => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                      "Invalid type attribute value for key action"))
-        }
-    }
-}
-
-impl ToJson for KeyAction {
-    fn to_json(&self) -> Json {
-        match self {
-            &KeyAction::Down(ref x) => x.to_json(),
-            &KeyAction::Up(ref x) => x.to_json(),
+impl<'a> From<&'a KeyAction> for Value {
+    fn from(params: &'a KeyAction) -> Value {
+        match *params {
+            KeyAction::Down(ref x) => x.into(),
+            KeyAction::Up(ref x) => x.into(),
         }
     }
 }
 
-fn validate_key_value(value_str: &str) -> WebDriverResult<String> {
-    let mut graphemes = value_str.graphemes(true);
-    let value = if let Some(g) = graphemes.next() {
-        g
-    } else {
-        return Err(WebDriverError::new(
-            ErrorStatus::InvalidArgument,
-            "Parameter 'value' was an empty string"))
-    };
-    if graphemes.next().is_some() {
-        return Err(WebDriverError::new(
-            ErrorStatus::InvalidArgument,
-            "Parameter 'value' contained multiple graphemes"))
-    };
-    Ok(value.to_string())
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct KeyUpAction {
     pub value: String
 }
 
-impl Parameters for KeyUpAction {
-    fn from_json(body: &Json) -> WebDriverResult<KeyUpAction> {
-        let value_str = try_opt!(
-                try_opt!(body.find("value"),
-                         ErrorStatus::InvalidArgument,
-                         "Missing value parameter").as_string(),
-                ErrorStatus::InvalidArgument,
-            "Parameter 'value' was not a string");
-
-        let value = try!(validate_key_value(value_str));
-        Ok(KeyUpAction {
-            value: value
-        })
+impl<'a> From<&'a KeyUpAction> for Value {
+    fn from(params: &'a KeyUpAction) -> Value {
+        let mut data = Map::new();
+        data.insert("type".to_owned(),
+                    "keyUp".into());
+        data.insert("value".to_string(),
+                    params.value.to_string().into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for KeyUpAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("type".to_owned(),
-                    "keyUp".to_json());
-        data.insert("value".to_string(),
-                    self.value.to_string().to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct KeyDownAction {
     pub value: String
 }
 
-impl Parameters for KeyDownAction {
-    fn from_json(body: &Json) -> WebDriverResult<KeyDownAction> {
-        let value_str = try_opt!(
-                try_opt!(body.find("value"),
-                         ErrorStatus::InvalidArgument,
-                         "Missing value parameter").as_string(),
-                ErrorStatus::InvalidArgument,
-            "Parameter 'value' was not a string");
-        let value = try!(validate_key_value(value_str));
-        Ok(KeyDownAction {
-            value: value
-        })
+impl<'a> From<&'a KeyDownAction> for Value {
+    fn from(params: &'a KeyDownAction) -> Value {
+        let mut data = Map::new();
+        data.insert("type".to_owned(),
+                    "keyDown".into());
+        data.insert("value".to_owned(),
+                    params.value.to_string().into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for KeyDownAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("type".to_owned(),
-                    "keyDown".to_json());
-        data.insert("value".to_owned(),
-                    self.value.to_string().to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged, rename_all="lowercase")]
 pub enum PointerOrigin {
     Viewport,
     Pointer,
     Element(WebElement),
 }
 
-impl Parameters for PointerOrigin {
-    fn from_json(body: &Json) -> WebDriverResult<PointerOrigin> {
-        match *body {
-            Json::String(ref x) => {
-                match &**x {
-                    "viewport" => Ok(PointerOrigin::Viewport),
-                    "pointer" => Ok(PointerOrigin::Pointer),
-                    _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                 "Unknown pointer origin"))
-                }
-            },
-            Json::Object(_) => Ok(PointerOrigin::Element(try!(WebElement::from_json(body)))),
-            _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                        "Pointer origin was not a string or an object"))
-        }
-    }
-}
-
-impl ToJson for PointerOrigin {
-    fn to_json(&self) -> Json {
-        match *self {
-            PointerOrigin::Viewport => "viewport".to_json(),
-            PointerOrigin::Pointer => "pointer".to_json(),
-            PointerOrigin::Element(ref x) => x.to_json(),
+impl<'a> From<&'a PointerOrigin> for Value {
+    fn from(params: &'a PointerOrigin) -> Value {
+        match *params {
+            PointerOrigin::Viewport => "viewport".into(),
+            PointerOrigin::Pointer => "pointer".into(),
+            PointerOrigin::Element(ref x) => x.into(),
         }
     }
 }
 
 impl Default for PointerOrigin {
     fn default() -> PointerOrigin {
         PointerOrigin::Viewport
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
 pub enum PointerAction {
+    #[serde(rename="pointerUp")]
     Up(PointerUpAction),
+    #[serde(rename="pointerDown")]
     Down(PointerDownAction),
+    #[serde(rename="pointerMove")]
     Move(PointerMoveAction),
+    #[serde(rename="pointerCancel")]
     Cancel
 }
 
-impl Parameters for PointerAction {
-    fn from_json(body: &Json) -> WebDriverResult<PointerAction> {
-        match body.find("type").and_then(|x| x.as_string()) {
-            Some("pointerUp") => Ok(PointerAction::Up(try!(PointerUpAction::from_json(body)))),
-            Some("pointerDown") => Ok(PointerAction::Down(try!(PointerDownAction::from_json(body)))),
-            Some("pointerMove") => Ok(PointerAction::Move(try!(PointerMoveAction::from_json(body)))),
-            Some("pointerCancel") => Ok(PointerAction::Cancel),
-            Some(_) | None => Err(WebDriverError::new(
-                ErrorStatus::InvalidArgument,
-                "Missing or invalid type argument for pointer action"))
-        }
-    }
-}
-
-impl ToJson for PointerAction {
-    fn to_json(&self) -> Json {
-        match self {
-            &PointerAction::Down(ref x) => x.to_json(),
-            &PointerAction::Up(ref x) => x.to_json(),
-            &PointerAction::Move(ref x) => x.to_json(),
-            &PointerAction::Cancel => {
-                let mut data = BTreeMap::new();
+impl<'a> From<&'a PointerAction> for Value {
+    fn from(params: &'a PointerAction) -> Value {
+        match *params {
+            PointerAction::Down(ref x) => x.into(),
+            PointerAction::Up(ref x) => x.into(),
+            PointerAction::Move(ref x) => x.into(),
+            PointerAction::Cancel => {
+                let mut data = Map::new();
                 data.insert("type".to_owned(),
-                            "pointerCancel".to_json());
-                Json::Object(data)
+                            "pointerCancel".into());
+                Value::Object(data)
             }
         }
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct PointerUpAction {
     pub button: u64,
 }
 
-impl Parameters for PointerUpAction {
-    fn from_json(body: &Json) -> WebDriverResult<PointerUpAction> {
-        let button = try_opt!(
-            try_opt!(body.find("button"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing button parameter").as_u64(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'button' was not a positive integer");
-
-        Ok(PointerUpAction {
-            button: button
-        })
+impl<'a> From<&'a PointerUpAction> for Value {
+    fn from(params: &'a PointerUpAction) -> Value {
+        let mut data = Map::new();
+        data.insert("type".to_owned(),
+                    "pointerUp".into());
+        data.insert("button".to_owned(), params.button.into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for PointerUpAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("type".to_owned(),
-                    "pointerUp".to_json());
-        data.insert("button".to_owned(), self.button.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct PointerDownAction {
     pub button: u64,
 }
 
-impl Parameters for PointerDownAction {
-    fn from_json(body: &Json) -> WebDriverResult<PointerDownAction> {
-        let button = try_opt!(
-            try_opt!(body.find("button"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing button parameter").as_u64(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'button' was not a positive integer");
-
-        Ok(PointerDownAction {
-            button: button
-        })
+impl<'a> From<&'a PointerDownAction> for Value {
+    fn from(params: &'a PointerDownAction) -> Value {
+        let mut data = Map::new();
+        data.insert("type".to_owned(),
+                    "pointerDown".into());
+        data.insert("button".to_owned(), params.button.into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for PointerDownAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("type".to_owned(),
-                    "pointerDown".to_json());
-        data.insert("button".to_owned(), self.button.to_json());
-        Json::Object(data)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct PointerMoveAction {
+    pub duration: Option<u64>,
+    pub origin: PointerOrigin,
+    pub x: Option<i64>,
+    pub y: Option<i64>
+}
+
+impl<'a> From<&'a PointerMoveAction> for Value {
+    fn from(params: &'a PointerMoveAction) -> Value {
+        let mut data = Map::new();
+        data.insert("type".to_owned(), "pointerMove".into());
+        if let Some(duration) = params.duration {
+            data.insert("duration".to_owned(),
+                        duration.into());
+        }
+
+        data.insert("origin".to_owned(), (&params.origin).into());
+
+        if let Some(x) = params.x {
+            data.insert("x".to_owned(), x.into());
+        }
+        if let Some(y) = params.y {
+            data.insert("y".to_owned(), y.into());
+        }
+        Value::Object(data)
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub struct PointerMoveAction {
-    pub duration: Nullable<u64>,
-    pub origin: PointerOrigin,
-    pub x: Nullable<i64>,
-    pub y: Nullable<i64>
-}
+#[cfg(test)]
+mod test {
+    use serde_json;
+    use command::ActionsParameters;
+    use common::WebElement;
+    use super::*;
 
-impl Parameters for PointerMoveAction {
-    fn from_json(body: &Json) -> WebDriverResult<PointerMoveAction> {
-        let duration = match body.find("duration") {
-            Some(duration) => Some(try_opt!(duration.as_u64(),
-                                            ErrorStatus::InvalidArgument,
-                                            "Parameter 'duration' was not a positive integer")),
-            None => None
-
-        };
-
-        let origin = match body.find("origin") {
-            Some(o) => try!(PointerOrigin::from_json(o)),
-            None => PointerOrigin::default()
-        };
-
-        let x = match body.find("x") {
-            Some(x) => {
-                Some(try_opt!(x.as_i64(),
-                              ErrorStatus::InvalidArgument,
-                              "Parameter 'x' was not an integer"))
-            },
-            None => None
+    #[test]
+    fn test_pointer_no_parameters() {
+        let expected = ActionsParameters {
+            actions: vec![
+                ActionSequence {
+                    id: None,
+                    actions: ActionsType::Pointer {
+                        parameters: PointerActionParameters {
+                            pointer_type: PointerType::Mouse
+                        },
+                        actions: vec!{
+                            PointerActionItem::Pointer (
+                                PointerAction::Down (
+                                    PointerDownAction {
+                                        button: 0
+                                    }
+                                )
+                            ),
+                            PointerActionItem::Pointer(
+                                PointerAction::Move (
+                                    PointerMoveAction {
+                                        duration: Some(100),
+                                        x: Some(5),
+                                        y: Some(10),
+                                        origin: PointerOrigin::Pointer
+                                    }
+                                )
+                            ),
+                            PointerActionItem::Pointer(
+                                PointerAction::Move (
+                                    PointerMoveAction {
+                                        duration: Some(200),
+                                        x: Some(10),
+                                        y: Some(20),
+                                        origin: PointerOrigin::Element(
+                                            WebElement {
+                                                id: "elem".into()
+                                            }
+                                        )
+                                    }
+                                )
+                            ),
+                            PointerActionItem::Pointer(
+                                PointerAction::Up (
+                                    PointerUpAction {
+                                        button: 0
+                                    }
+                                )
+                            ),
+                            PointerActionItem::Pointer(
+                                PointerAction::Cancel
+                            ),
+                        }
+                    }
+                }
+            ]
         };
 
-        let y = match body.find("y") {
-            Some(y) => {
-                Some(try_opt!(y.as_i64(),
-                              ErrorStatus::InvalidArgument,
-                              "Parameter 'y' was not an integer"))
-            },
-            None => None
-        };
-
-        Ok(PointerMoveAction {
-            duration: duration.into(),
-            origin: origin.into(),
-            x: x.into(),
-            y: y.into(),
-        })
+        let data = r#"{
+"actions": [
+    {"type": "pointer", "actions": [
+    {"type": "pointerDown", "button": 0},
+    {"type": "pointerMove", "x": 5, "y": 10, "origin": "relative"},
+    {"type": "pointerMove", "x": 5, "y": 10, "origin": {"element-6066-11e4-a52e-4f735466cecf": "elem"}},
+    {"type": "pointerUp", "button": 0},
+    {"type": "pointerCancel"}
+  ]
+}]}"#;
+        let actual: ActionsParameters = serde_json::from_str(&data).unwrap();
+        assert_eq!(expected, actual);
     }
 }
-
-impl ToJson for PointerMoveAction {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("type".to_owned(), "pointerMove".to_json());
-        if self.duration.is_value() {
-            data.insert("duration".to_owned(),
-                        self.duration.to_json());
-        }
-
-        data.insert("origin".to_owned(), self.origin.to_json());
-
-        if self.x.is_value() {
-            data.insert("x".to_owned(), self.x.to_json());
-        }
-        if self.y.is_value() {
-            data.insert("y".to_owned(), self.y.to_json());
-        }
-        Json::Object(data)
-    }
-}
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -1,15 +1,14 @@
-use command::Parameters;
-use error::{ErrorStatus, WebDriverError, WebDriverResult};
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
+use error::{WebDriverResult, WebDriverError, ErrorStatus};
+use std::convert::From;
+use serde_json::{Value, Map};
 use url::Url;
 
-pub type Capabilities = BTreeMap<String, Json>;
+pub type Capabilities = Map<String, Value>;
 
 /// Trait for objects that can be used to inspect browser capabilities
 ///
 /// The main methods in this trait are called with a Capabilites object
 /// resulting from a full set of potential capabilites for the session.
 /// Given those Capabilities they return a property of the browser instance
 /// that would be initiated. In many cases this will be independent of the
 /// input, but in the case of e.g. browser version, it might depend on a
@@ -29,59 +28,71 @@ pub trait BrowserCapabilities {
     /// Parameters are the actual browser version and the comparison string,
     /// respectively. The format of the comparison string is implementation-defined.
     fn compare_browser_version(&mut self, version: &str, comparison: &str) -> WebDriverResult<bool>;
     /// Name of the platform/OS
     fn platform_name(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
     /// Whether insecure certificates are supported
     fn accept_insecure_certs(&mut self, &Capabilities) -> WebDriverResult<bool>;
 
-    fn accept_proxy(&mut self, proxy_settings: &BTreeMap<String, Json>, &Capabilities) -> WebDriverResult<bool>;
+    fn accept_proxy(&mut self, proxy_settings: &Map<String, Value>, &Capabilities) -> WebDriverResult<bool>;
 
     /// Type check custom properties
     ///
     /// Check that custom properties containing ":" have the correct data types.
     /// Properties that are unrecognised must be ignored i.e. return without
     /// error.
-    fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()>;
+    fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()>;
     /// Check if custom properties are accepted capabilites
     ///
     /// Check that custom properties containing ":" are compatible with
     /// the implementation.
-    fn accept_custom(&mut self, name: &str, value: &Json, merged: &Capabilities) -> WebDriverResult<bool>;
+    fn accept_custom(&mut self, name: &str, value: &Value, merged: &Capabilities) -> WebDriverResult<bool>;
 }
 
 /// Trait to abstract over various version of the new session parameters
 ///
 /// This trait is expected to be implemented on objects holding the capabilities
 /// from a new session command.
 pub trait CapabilitiesMatching {
     /// Match the BrowserCapabilities against some candidate capabilites
     ///
     /// Takes a BrowserCapabilites object and returns a set of capabilites that
     /// are valid for that browser, if any, or None if there are no matching
     /// capabilities.
     fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
                                              -> WebDriverResult<Option<Capabilities>>;
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
+pub struct SpecNewSessionParametersWrapper {
+    capabilities: SpecNewSessionParameters
+}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
 pub struct SpecNewSessionParameters {
     pub alwaysMatch: Capabilities,
     pub firstMatch: Vec<Capabilities>,
 }
 
+impl From<SpecNewSessionParametersWrapper> for SpecNewSessionParameters {
+    fn from(wrapper: SpecNewSessionParametersWrapper) -> SpecNewSessionParameters {
+        wrapper.capabilities
+    }
+}
+
+
 impl SpecNewSessionParameters {
     fn validate<T: BrowserCapabilities>(&self,
                                         mut capabilities: Capabilities,
                                         browser_capabilities: &T) -> WebDriverResult<Capabilities> {
         // Filter out entries with the value `null`
         let null_entries = capabilities
             .iter()
-            .filter(|&(_, ref value)| **value == Json::Null)
+            .filter(|&(_, ref value)| **value == Value::Null)
             .map(|(k, _)| k.clone())
             .collect::<Vec<String>>();
         for key in null_entries {
             capabilities.remove(&key);
         }
 
         for (key, value) in capabilities.iter() {
             match &**key {
@@ -115,19 +126,19 @@ impl SpecNewSessionParameters {
                         try!(browser_capabilities.validate_custom(x, value));
                     }
                 }
             }
         }
         Ok(capabilities)
     }
 
-    fn validate_page_load_strategy(value: &Json) -> WebDriverResult<()> {
+    fn validate_page_load_strategy(value: &Value) -> WebDriverResult<()> {
         match value {
-            &Json::String(ref x) => {
+            &Value::String(ref x) => {
                 match &**x {
                     "normal" |
                     "eager" |
                     "none" => {},
                     x => {
                         return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
                             format!("Invalid page load strategy: {}", x)))
@@ -135,38 +146,38 @@ impl SpecNewSessionParameters {
                 }
             }
             _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                 "pageLoadStrategy is not a string"))
         }
         Ok(())
     }
 
-    fn validate_proxy(proxy_value: &Json) -> WebDriverResult<()> {
+    fn validate_proxy(proxy_value: &Value) -> WebDriverResult<()> {
         let obj = try_opt!(proxy_value.as_object(),
                            ErrorStatus::InvalidArgument,
                            "proxy is not an object");
 
         for (key, value) in obj.iter() {
             match &**key {
-                "proxyType" => match value.as_string() {
+                "proxyType" => match value.as_str() {
                     Some("pac") |
                     Some("direct") |
                     Some("autodetect") |
                     Some("system") |
                     Some("manual") => {},
                     Some(x) => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("Invalid proxyType value: {}", x))),
                     None => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("proxyType is not a string: {}", value))),
                 },
 
-                "proxyAutoconfigUrl" => match value.as_string() {
+                "proxyAutoconfigUrl" => match value.as_str() {
                     Some(x) => {
                         Url::parse(x).or(Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
                             format!("proxyAutoconfigUrl is not a valid URL: {}", x))))?;
                     },
                     None => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "proxyAutoconfigUrl is not a string"
@@ -189,21 +200,21 @@ impl SpecNewSessionParameters {
                     ErrorStatus::InvalidArgument,
                     format!("Invalid proxy configuration entry: {}", x)))
             }
         }
 
         Ok(())
     }
 
-    fn validate_no_proxy(value: &Json) -> WebDriverResult<()> {
+    fn validate_no_proxy(value: &Value) -> WebDriverResult<()> {
         match value.as_array() {
             Some(hosts) => {
                 for host in hosts {
-                    match host.as_string() {
+                    match host.as_str() {
                         Some(_) => {},
                         None => return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
                             format!("noProxy item is not a string: {}", host)
                         ))
                     }
                 }
             },
@@ -213,18 +224,18 @@ impl SpecNewSessionParameters {
             ))
         }
 
         Ok(())
     }
 
     /// Validate whether a named capability is JSON value is a string containing a host
     /// and possible port
-    fn validate_host(value: &Json, entry: &str) -> WebDriverResult<()> {
-        match value.as_string() {
+    fn validate_host(value: &Value, entry: &str) -> WebDriverResult<()> {
+        match value.as_str() {
             Some(host) => {
                 if host.contains("://") {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("{} must not contain a scheme: {}", entry, host)));
                 }
 
                 // Temporarily add a scheme so the host can be parsed as URL
@@ -248,17 +259,17 @@ impl SpecNewSessionParameters {
                 ErrorStatus::InvalidArgument,
                 format!("{} is not a string: {}", entry, value)
             ))
         }
 
         Ok(())
     }
 
-    fn validate_timeouts(value: &Json) -> WebDriverResult<()> {
+    fn validate_timeouts(value: &Value) -> WebDriverResult<()> {
         let obj = try_opt!(
             value.as_object(),
             ErrorStatus::InvalidArgument,
             "timeouts capability is not an object"
         );
 
         for (key, value) in obj.iter() {
             match &**key {
@@ -283,19 +294,19 @@ impl SpecNewSessionParameters {
                     ))
                 }
             }
         }
 
         Ok(())
     }
 
-    fn validate_unhandled_prompt_behaviour(value: &Json) -> WebDriverResult<()> {
+    fn validate_unhandled_prompt_behaviour(value: &Value) -> WebDriverResult<()> {
         let behaviour = try_opt!(
-            value.as_string(),
+            value.as_str(),
             ErrorStatus::InvalidArgument,
             format!("unhandledPromptBehavior is not a string: {}", value)
         );
 
         match behaviour {
             "dismiss" | "accept" => {}
             x => {
                 return Err(WebDriverError::new(
@@ -304,101 +315,55 @@ impl SpecNewSessionParameters {
                 ))
             }
         }
 
         Ok(())
     }
 }
 
-impl Parameters for SpecNewSessionParameters {
-    fn from_json(body: &Json) -> WebDriverResult<SpecNewSessionParameters> {
-        let data = try_opt!(
-            body.as_object(),
-            ErrorStatus::UnknownError,
-            format!("Malformed capabilities, message body is not an object: {}", body)
-        );
-
-        let capabilities = try_opt!(
-            try_opt!(
-                data.get("capabilities"),
-                ErrorStatus::InvalidArgument,
-                "Malformed capabilities, missing \"capabilities\" field"
-            ).as_object(),
-            ErrorStatus::InvalidArgument,
-            "Malformed capabilities, \"capabilities\" field is not an object}"
-        );
-
-        let default_always_match = Json::Object(Capabilities::new());
-        let always_match = try_opt!(
-            capabilities
-                .get("alwaysMatch")
-                .unwrap_or(&default_always_match)
-                .as_object(),
-            ErrorStatus::InvalidArgument,
-            "Malformed capabilities, alwaysMatch field is not an object"
-        );
-        let default_first_matches = Json::Array(vec![]);
-        let first_matches = try_opt!(
-            capabilities
-                .get("firstMatch")
-                .unwrap_or(&default_first_matches)
-                .as_array(),
-            ErrorStatus::InvalidArgument,
-            "Malformed capabilities, firstMatch field is not an array"
-        ).iter()
-            .map(|x| {
-                x.as_object().map(|x| x.clone()).ok_or(WebDriverError::new(
-                    ErrorStatus::InvalidArgument,
-                    "Malformed capabilities, firstMatch entry is not an object",
-                ))
-            })
-            .collect::<WebDriverResult<Vec<Capabilities>>>()?;
-
-        return Ok(SpecNewSessionParameters {
-            alwaysMatch: always_match.clone(),
-            firstMatch: first_matches,
-        });
-    }
-}
-
-impl ToJson for SpecNewSessionParameters {
-    fn to_json(&self) -> Json {
-        let mut body = BTreeMap::new();
-        let mut capabilities = BTreeMap::new();
-        capabilities.insert("alwaysMatch".into(), self.alwaysMatch.to_json());
-        capabilities.insert("firstMatch".into(), self.firstMatch.to_json());
-        body.insert("capabilities".into(), capabilities.to_json());
-        Json::Object(body)
+impl<'a> From<&'a SpecNewSessionParameters> for Value {
+    fn from(params: &'a SpecNewSessionParameters) -> Value {
+        let mut body = Map::new();
+        let mut capabilities = Map::new();
+        capabilities.insert("alwaysMatch".into(), Value::Object(params.alwaysMatch.clone()));
+        capabilities.insert("firstMatch".into(), Value::Array(params.firstMatch.clone()
+                                                              .into_iter()
+                                                              .map(|x| Value::Object(x))
+                                                              .collect::<Vec<_>>()));
+        body.insert("capabilities".into(), Value::Object(capabilities));
+        Value::Object(body)
     }
 }
 
 impl CapabilitiesMatching for SpecNewSessionParameters {
     fn match_browser<T: BrowserCapabilities>(
         &self,
         browser_capabilities: &mut T,
     ) -> WebDriverResult<Option<Capabilities>> {
-        let default = vec![BTreeMap::new()];
+        let default = vec![Map::new()];
         let capabilities_list = if self.firstMatch.len() > 0 {
             &self.firstMatch
         } else {
             &default
         };
 
         let merged_capabilities = capabilities_list
             .iter()
             .map(|first_match_entry| {
                 if first_match_entry.keys().any(|k| self.alwaysMatch.contains_key(k)) {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "firstMatch key shadowed a value in alwaysMatch",
                     ));
                 }
                 let mut merged = self.alwaysMatch.clone();
-                merged.append(&mut first_match_entry.clone());
+                for (key, value) in first_match_entry.clone().into_iter() {
+                    merged.insert(key, value);
+                }
                 Ok(merged)
             })
             .map(|merged| {
                 merged.and_then(|x| self.validate(x, browser_capabilities))
             })
             .collect::<WebDriverResult<Vec<Capabilities>>>()?;
 
         let selected = merged_capabilities
@@ -409,58 +374,58 @@ impl CapabilitiesMatching for SpecNewSes
                 for (key, value) in merged.iter() {
                     match &**key {
                         "browserName" => {
                             let browserValue = browser_capabilities
                                 .browser_name(merged)
                                 .ok()
                                 .and_then(|x| x);
 
-                            if value.as_string() != browserValue.as_ref().map(|x| &**x) {
+                            if value.as_str() != browserValue.as_ref().map(|x| &**x) {
                                 return None;
                             }
                         }
                         "browserVersion" => {
                             let browserValue = browser_capabilities
                                 .browser_version(merged)
                                 .ok()
                                 .and_then(|x| x);
                             // We already validated this was a string
-                            let version_cond = value.as_string().unwrap_or("");
+                            let version_cond = value.as_str().unwrap_or("");
                             if let Some(version) = browserValue {
                                 if !browser_capabilities
                                     .compare_browser_version(&*version, version_cond)
                                     .unwrap_or(false)
                                 {
                                     return None;
                                 }
                             } else {
                                 return None;
                             }
                         }
                         "platformName" => {
                             let browserValue = browser_capabilities
                                 .platform_name(merged)
                                 .ok()
                                 .and_then(|x| x);
-                            if value.as_string() != browserValue.as_ref().map(|x| &**x) {
+                            if value.as_str() != browserValue.as_ref().map(|x| &**x) {
                                 return None;
                             }
                         }
                         "acceptInsecureCerts" => {
-                            if value.as_boolean().unwrap_or(false) &&
+                            if value.as_bool().unwrap_or(false) &&
                                 !browser_capabilities
                                     .accept_insecure_certs(merged)
                                     .unwrap_or(false)
                             {
                                 return None;
                             }
                         }
                         "proxy" => {
-                            let default = BTreeMap::new();
+                            let default = Map::new();
                             let proxy = value.as_object().unwrap_or(&default);
                             if !browser_capabilities
                                 .accept_proxy(&proxy, merged)
                                 .unwrap_or(false)
                             {
                                 return None;
                             }
                         }
@@ -482,93 +447,61 @@ impl CapabilitiesMatching for SpecNewSes
                 return Some(merged);
             })
             .next()
             .map(|x| x.clone());
         Ok(selected)
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
 pub struct LegacyNewSessionParameters {
     pub desired: Capabilities,
     pub required: Capabilities,
 }
 
 impl CapabilitiesMatching for LegacyNewSessionParameters {
     fn match_browser<T: BrowserCapabilities>(
         &self,
         browser_capabilities: &mut T,
     ) -> WebDriverResult<Option<Capabilities>> {
         // For now don't do anything much, just merge the
         // desired and required and return the merged list.
 
-        let mut capabilities: Capabilities = BTreeMap::new();
+        let mut capabilities: Capabilities = Map::new();
         self.required.iter().chain(self.desired.iter()).fold(
             &mut capabilities,
             |caps, (key, value)| {
                 if !caps.contains_key(key) {
                     caps.insert(key.clone(), value.clone());
                 }
                 caps
             },
         );
         browser_capabilities.init(&capabilities);
         Ok(Some(capabilities))
     }
 }
 
-impl Parameters for LegacyNewSessionParameters {
-    fn from_json(body: &Json) -> WebDriverResult<LegacyNewSessionParameters> {
-        let data = try_opt!(
-            body.as_object(),
-            ErrorStatus::UnknownError,
-            format!("Malformed legacy capabilities, message body is not an object: {}", body)
-        );
-
-        let desired = if let Some(capabilities) = data.get("desiredCapabilities") {
-            try_opt!(
-                capabilities.as_object(),
-                ErrorStatus::InvalidArgument,
-                "Malformed legacy capabilities, desiredCapabilities field is not an object"
-            ).clone()
-        } else {
-            BTreeMap::new()
-        };
-
-        let required = if let Some(capabilities) = data.get("requiredCapabilities") {
-            try_opt!(
-                capabilities.as_object(),
-                ErrorStatus::InvalidArgument,
-                "Malformed legacy capabilities, requiredCapabilities field is not an object"
-            ).clone()
-        } else {
-            BTreeMap::new()
-        };
-
-        Ok(LegacyNewSessionParameters { desired, required })
-    }
-}
-
-impl ToJson for LegacyNewSessionParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("desiredCapabilities".to_owned(), self.desired.to_json());
-        data.insert("requiredCapabilities".to_owned(), self.required.to_json());
-        Json::Object(data)
+impl<'a> From<&'a LegacyNewSessionParameters> for Value {
+    fn from(params: &'a LegacyNewSessionParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("desiredCapabilities".to_owned(), Value::Object(params.desired.clone()));
+        data.insert("requiredCapabilities".to_owned(), Value::Object(params.required.clone()));
+        Value::Object(data)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use rustc_serialize::json::Json;
+    use serde_json::{self, Value};
     use super::{SpecNewSessionParameters, WebDriverResult};
 
     fn validate_proxy(value: &str) -> WebDriverResult<()> {
-        let data = Json::from_str(value).unwrap();
+        let data = serde_json::from_str::<Value>(value).unwrap();
         SpecNewSessionParameters::validate_proxy(&data)
     }
 
     #[test]
     fn test_validate_proxy() {
         // proxy hosts
         validate_proxy("{\"httpProxy\": \"127.0.0.1\"}").unwrap();
         validate_proxy("{\"httpProxy\": \"127.0.0.1:\"}").unwrap();
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -1,18 +1,17 @@
-use actions::{ActionSequence};
-use capabilities::{SpecNewSessionParameters, LegacyNewSessionParameters,
+use actions::ActionSequence;
+use capabilities::{SpecNewSessionParametersWrapper, SpecNewSessionParameters, LegacyNewSessionParameters,
                    CapabilitiesMatching, BrowserCapabilities, Capabilities};
-use common::{Date, Nullable, WebElement, FrameId, LocatorStrategy};
+use common::{Date, WebElement, FrameId, LocatorStrategy};
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 use httpapi::{Route, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
 use regex::Captures;
-use rustc_serialize::json;
-use rustc_serialize::json::{ToJson, Json};
-use std::collections::BTreeMap;
+use serde_json::{self, Value, Map};
+use std::convert::From;
 
 #[derive(Debug, PartialEq)]
 pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
     NewSession(NewSessionParameters),
     DeleteSession,
     Get(GetParameters),
     GetCurrentUrl,
     GoBack,
@@ -60,30 +59,30 @@ pub enum WebDriverCommand<T: WebDriverEx
     ElementSendKeys(WebElement, SendKeysParameters),
     PerformActions(ActionsParameters),
     ReleaseActions,
     DismissAlert,
     AcceptAlert,
     GetAlertText,
     SendAlertText(SendKeysParameters),
     TakeScreenshot,
-    TakeElementScreenshot(WebElement),
+    TakeElementScreenshot(TakeScreenshotParameters),
     Status,
     Extension(T)
 }
 
 pub trait WebDriverExtensionCommand : Clone + Send + PartialEq {
-    fn parameters_json(&self) -> Option<Json>;
+    fn parameters_json(&self) -> Option<Value>;
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct VoidWebDriverExtensionCommand;
 
 impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
-    fn parameters_json(&self) -> Option<Json> {
+    fn parameters_json(&self) -> Option<Value> {
         panic!("No extensions implemented");
     }
 }
 
 #[derive(Debug, PartialEq)]
 pub struct WebDriverMessage <U: WebDriverExtensionRoute=VoidWebDriverExtensionRoute> {
     pub session_id: Option<String>,
     pub command: WebDriverCommand<U::Command>,
@@ -101,80 +100,79 @@ impl<U: WebDriverExtensionRoute> WebDriv
 
     pub fn from_http(match_type: Route<U>,
                      params: &Captures,
                      raw_body: &str,
                      requires_body: bool)
                      -> WebDriverResult<WebDriverMessage<U>> {
         let session_id = WebDriverMessage::<U>::get_session_id(params);
         let body_data = try!(WebDriverMessage::<U>::decode_body(raw_body, requires_body));
-
         let command = match match_type {
             Route::NewSession => {
-                let parameters: NewSessionParameters = try!(Parameters::from_json(&body_data));
-                WebDriverCommand::NewSession(parameters)
+                let parameters: NewSessionParametersWrapper = serde_json::from_str(raw_body)?;
+                WebDriverCommand::NewSession(parameters.into())
             },
             Route::DeleteSession => WebDriverCommand::DeleteSession,
             Route::Get => {
-                let parameters: GetParameters = try!(Parameters::from_json(&body_data));
+                let parameters: GetParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::Get(parameters)
             },
             Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
             Route::GoBack => WebDriverCommand::GoBack,
             Route::GoForward => WebDriverCommand::GoForward,
             Route::Refresh => WebDriverCommand::Refresh,
             Route::GetTitle => WebDriverCommand::GetTitle,
             Route::GetPageSource => WebDriverCommand::GetPageSource,
             Route::GetWindowHandle => WebDriverCommand::GetWindowHandle,
             Route::GetWindowHandles => WebDriverCommand::GetWindowHandles,
             Route::CloseWindow => WebDriverCommand::CloseWindow,
             Route::GetTimeouts => WebDriverCommand::GetTimeouts,
             Route::SetTimeouts => {
-                let parameters: TimeoutsParameters = try!(Parameters::from_json(&body_data));
+                let parameters: TimeoutsParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::SetTimeouts(parameters)
             },
             Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => WebDriverCommand::GetWindowRect,
             Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
-                let parameters: WindowRectParameters = Parameters::from_json(&body_data)?;
+                let parameters: WindowRectParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::SetWindowRect(parameters)
             },
             Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
             Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
             Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
             Route::SwitchToWindow => {
-                let parameters: SwitchToWindowParameters = try!(Parameters::from_json(&body_data));
+                let parameters: SwitchToWindowParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::SwitchToWindow(parameters)
             }
             Route::SwitchToFrame => {
-                let parameters: SwitchToFrameParameters = try!(Parameters::from_json(&body_data));
+                let parameters: SwitchToFrameParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::SwitchToFrame(parameters)
             },
             Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
             Route::FindElement => {
-                let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
+                let parameters: LocatorParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::FindElement(parameters)
             },
             Route::FindElements => {
-                let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
+                let parameters: LocatorParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::FindElements(parameters)
             },
             Route::FindElementElement => {
                 let element_id = try_opt!(params.name("elementId"),
                                           ErrorStatus::InvalidArgument,
                                           "Missing elementId parameter");
                 let element = WebElement::new(element_id.as_str().into());
-                let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
+                let parameters: LocatorParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::FindElementElement(element, parameters)
             },
             Route::FindElementElements => {
                 let element_id = try_opt!(params.name("elementId"),
                                           ErrorStatus::InvalidArgument,
                                           "Missing elementId parameter");
                 let element = WebElement::new(element_id.as_str().into());
-                let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
+                let parameters: LocatorParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::FindElementElements(element, parameters)
             },
             Route::GetActiveElement => WebDriverCommand::GetActiveElement,
             Route::IsDisplayed => {
                 let element_id = try_opt!(params.name("elementId"),
                                           ErrorStatus::InvalidArgument,
                                           "Missing elementId parameter");
                 let element = WebElement::new(element_id.as_str().into());
@@ -266,116 +264,115 @@ impl<U: WebDriverExtensionRoute> WebDriv
                 let element = WebElement::new(element_id.as_str().into());
                 WebDriverCommand::ElementClear(element)
             },
             Route::ElementSendKeys => {
                 let element_id = try_opt!(params.name("elementId"),
                                           ErrorStatus::InvalidArgument,
                                           "Missing elementId parameter");
                 let element = WebElement::new(element_id.as_str().into());
-                let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
+                let parameters: SendKeysParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::ElementSendKeys(element, parameters)
             },
             Route::ExecuteScript => {
-                let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
+                let parameters: JavascriptCommandParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::ExecuteScript(parameters)
             },
             Route::ExecuteAsyncScript => {
-                let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
+                let parameters: JavascriptCommandParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::ExecuteAsyncScript(parameters)
             },
             Route::GetCookies => {
                 WebDriverCommand::GetCookies
             },
             Route::GetNamedCookie => {
                 let name = try_opt!(params.name("name"),
                                     ErrorStatus::InvalidArgument,
                                     "Missing 'name' parameter").as_str().into();
                 WebDriverCommand::GetNamedCookie(name)
             },
             Route::AddCookie => {
-                let parameters: AddCookieParameters = try!(Parameters::from_json(&body_data));
-                WebDriverCommand::AddCookie(parameters)
+                let parameters: AddCookieParametersWrapper = serde_json::from_str(raw_body)?;
+                WebDriverCommand::AddCookie(parameters.cookie)
             },
             Route::DeleteCookies => {
                 WebDriverCommand::DeleteCookies
             },
             Route::DeleteCookie => {
                 let name = try_opt!(params.name("name"),
                                     ErrorStatus::InvalidArgument,
                                     "Missing name parameter").as_str().into();
                 WebDriverCommand::DeleteCookie(name)
             },
             Route::PerformActions => {
-                let parameters: ActionsParameters = try!(Parameters::from_json(&body_data));
+                let parameters: ActionsParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::PerformActions(parameters)
             },
             Route::ReleaseActions => {
                 WebDriverCommand::ReleaseActions
             },
             Route::DismissAlert => {
                 WebDriverCommand::DismissAlert
             },
             Route::AcceptAlert => {
                 WebDriverCommand::AcceptAlert
             },
             Route::GetAlertText => {
                 WebDriverCommand::GetAlertText
             },
             Route::SendAlertText => {
-                let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
+                let parameters: SendKeysParameters = serde_json::from_str(raw_body)?;
                 WebDriverCommand::SendAlertText(parameters)
             },
             Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
             Route::TakeElementScreenshot =>  {
-                let element_id = try_opt!(params.name("elementId"),
-                                          ErrorStatus::InvalidArgument,
-                                          "Missing elementId parameter");
-                let element = WebElement::new(element_id.as_str().into());
-                WebDriverCommand::TakeElementScreenshot(element)
+                let parameters: TakeScreenshotParameters = serde_json::from_str(raw_body)?;
+                WebDriverCommand::TakeElementScreenshot(parameters)
             },
             Route::Status => WebDriverCommand::Status,
             Route::Extension(ref extension) => {
                 try!(extension.command(params, &body_data))
             }
         };
         Ok(WebDriverMessage::new(session_id, command))
     }
 
     fn get_session_id(params: &Captures) -> Option<String> {
         params.name("sessionId").map(|x| x.as_str().into())
     }
 
-    fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Json> {
+    fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Value> {
         if requires_body {
-            match Json::from_str(body) {
-                Ok(x @ Json::Object(_)) => Ok(x),
+            match serde_json::from_str(body) {
+                Ok(x @ Value::Object(_)) => Ok(x),
                 Ok(_) => {
                     Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                             "Body was not a JSON Object"))
                 }
-                Err(json::ParserError::SyntaxError(_, line, col)) => {
-                    let msg = format!("Failed to decode request as JSON: \"{}\"", body);
-                    let stack = format!("Syntax error at :{}:{}", line, col);
-                    Err(WebDriverError::new_with_stack(ErrorStatus::InvalidArgument, msg, stack))
-                }
-                Err(json::ParserError::IoError(e)) => {
-                    Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                            format!("I/O error whilst decoding body: {}", e)))
+                Err(e) => {
+                    if e.is_io() {
+                        Err(WebDriverError::new(ErrorStatus::InvalidArgument,
+                                                format!("I/O error whilst decoding body: {}", e)))
+                    } else {
+                        let msg = format!("Failed to decode request as JSON: {}", body);
+                        let stack = format!("Syntax error at :{}:{}", e.line(), e.column());
+                        Err(WebDriverError::new_with_stack(ErrorStatus::InvalidArgument,
+                                                           msg, stack))
+                    }
                 }
             }
         } else {
-            Ok(Json::Null)
+            Ok(Value::Null)
         }
     }
 }
 
-impl <U:WebDriverExtensionRoute> ToJson for WebDriverMessage<U> {
-    fn to_json(&self) -> Json {
-        let parameters = match self.command {
+impl <U:WebDriverExtensionRoute> From<WebDriverMessage<U>> for Value {
+    fn from(msg: WebDriverMessage<U>) -> Value {
+        let parameters = match msg.command {
             WebDriverCommand::AcceptAlert |
             WebDriverCommand::CloseWindow |
             WebDriverCommand::ReleaseActions |
             WebDriverCommand::DeleteCookie(_) |
             WebDriverCommand::DeleteCookies |
             WebDriverCommand::DeleteSession |
             WebDriverCommand::DismissAlert |
             WebDriverCommand::ElementClear(_) |
@@ -410,728 +407,336 @@ impl <U:WebDriverExtensionRoute> ToJson 
             WebDriverCommand::Refresh |
             WebDriverCommand::Status |
             WebDriverCommand::SwitchToParentFrame |
             WebDriverCommand::TakeElementScreenshot(_) |
             WebDriverCommand::TakeScreenshot => {
                 None
             },
 
-            WebDriverCommand::AddCookie(ref x) => Some(x.to_json()),
-            WebDriverCommand::ElementSendKeys(_, ref x) => Some(x.to_json()),
+            WebDriverCommand::AddCookie(ref x) => Some(x.into()),
+            WebDriverCommand::ElementSendKeys(_, ref x) => Some(x.into()),
             WebDriverCommand::ExecuteAsyncScript(ref x) |
-            WebDriverCommand::ExecuteScript(ref x) => Some(x.to_json()),
-            WebDriverCommand::FindElementElement(_, ref x) => Some(x.to_json()),
-            WebDriverCommand::FindElementElements(_, ref x) => Some(x.to_json()),
-            WebDriverCommand::FindElement(ref x) => Some(x.to_json()),
-            WebDriverCommand::FindElements(ref x) => Some(x.to_json()),
-            WebDriverCommand::Get(ref x) => Some(x.to_json()),
-            WebDriverCommand::PerformActions(ref x) => Some(x.to_json()),
-            WebDriverCommand::SendAlertText(ref x) => Some(x.to_json()),
-            WebDriverCommand::SetTimeouts(ref x) => Some(x.to_json()),
-            WebDriverCommand::SetWindowRect(ref x) => Some(x.to_json()),
-            WebDriverCommand::SwitchToFrame(ref x) => Some(x.to_json()),
-            WebDriverCommand::SwitchToWindow(ref x) => Some(x.to_json()),
+            WebDriverCommand::ExecuteScript(ref x) => Some(x.into()),
+            WebDriverCommand::FindElementElement(_, ref x) => Some(x.into()),
+            WebDriverCommand::FindElementElements(_, ref x) => Some(x.into()),
+            WebDriverCommand::FindElement(ref x) => Some(x.into()),
+            WebDriverCommand::FindElements(ref x) => Some(x.into()),
+            WebDriverCommand::Get(ref x) => Some(x.into()),
+            WebDriverCommand::PerformActions(ref x) => Some(x.into()),
+            WebDriverCommand::SendAlertText(ref x) => Some(x.into()),
+            WebDriverCommand::SetTimeouts(ref x) => Some(x.into()),
+            WebDriverCommand::SetWindowRect(ref x) => Some(x.into()),
+            WebDriverCommand::SwitchToFrame(ref x) => Some(x.into()),
+            WebDriverCommand::SwitchToWindow(ref x) => Some(x.into()),
             WebDriverCommand::Extension(ref x) => x.parameters_json(),
         };
 
-        let mut data = BTreeMap::new();
+        let mut data = Map::new();
         if let Some(parameters) = parameters {
             data.insert("parameters".to_string(), parameters);
         }
-        Json::Object(data)
+        Value::Object(data)
     }
 }
 
-pub trait Parameters: Sized {
-    fn from_json(body: &Json) -> WebDriverResult<Self>;
-}
-
 /// Wrapper around the two supported variants of new session paramters
 ///
 /// The Spec variant is used for storing spec-compliant parameters whereas
 /// the legacy variant is used to store desiredCapabilities/requiredCapabilities
 /// parameters, and is intended to minimise breakage as we transition users to
 /// the spec design.
+
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(untagged)]
+pub enum NewSessionParametersWrapper {
+    Spec(SpecNewSessionParametersWrapper),
+    Legacy(LegacyNewSessionParameters),
+}
+
 #[derive(Debug, PartialEq)]
 pub enum NewSessionParameters {
     Spec(SpecNewSessionParameters),
     Legacy(LegacyNewSessionParameters),
 }
 
-impl Parameters for NewSessionParameters {
-    fn from_json(body: &Json) -> WebDriverResult<NewSessionParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body was not an object");
-        if data.get("capabilities").is_some() {
-            Ok(NewSessionParameters::Spec(try!(SpecNewSessionParameters::from_json(body))))
-        } else {
-            Ok(NewSessionParameters::Legacy(try!(LegacyNewSessionParameters::from_json(body))))
+impl From<NewSessionParametersWrapper> for NewSessionParameters {
+    fn from(wrapper: NewSessionParametersWrapper) -> NewSessionParameters {
+        match wrapper {
+            NewSessionParametersWrapper::Spec(x) => NewSessionParameters::Spec(x.into()),
+            NewSessionParametersWrapper::Legacy(x) => NewSessionParameters::Legacy(x)
         }
     }
 }
 
-impl ToJson for NewSessionParameters {
-    fn to_json(&self) -> Json {
-        match self {
-            &NewSessionParameters::Spec(ref x) => x.to_json(),
-            &NewSessionParameters::Legacy(ref x) => x.to_json()
+
+impl <'a> From<&'a NewSessionParameters> for Value {
+    fn from(params: &'a NewSessionParameters) -> Value {
+        match *params {
+            NewSessionParameters::Spec(ref x) => x.into(),
+            NewSessionParameters::Legacy(ref x) => x.into()
         }
     }
 }
 
 impl CapabilitiesMatching for NewSessionParameters {
     fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
                                              -> WebDriverResult<Option<Capabilities>> {
         match self {
             &NewSessionParameters::Spec(ref x) => x.match_browser(browser_capabilities),
             &NewSessionParameters::Legacy(ref x) => x.match_browser(browser_capabilities)
         }
     }
 }
 
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct GetParameters {
     pub url: String
 }
 
-impl Parameters for GetParameters {
-    fn from_json(body: &Json) -> WebDriverResult<GetParameters> {
-        let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
-                            "Message body was not an object");
-        let url = try_opt!(
-            try_opt!(data.get("url"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'url' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "'url' not a string");
-        Ok(GetParameters {
-            url: url.to_string()
-        })
+impl<'a> From<&'a GetParameters> for Value {
+    fn from(params: &'a GetParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("url".to_string(), Value::String(params.url.clone()));
+        Value::Object(data)
     }
 }
 
-impl ToJson for GetParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("url".to_string(), self.url.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct TimeoutsParameters {
     pub script: Option<u64>,
+    #[serde(rename="pageLoad")]
     pub page_load: Option<u64>,
     pub implicit: Option<u64>,
 }
 
-impl Parameters for TimeoutsParameters {
-    fn from_json(body: &Json) -> WebDriverResult<TimeoutsParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body was not an object");
-
-        let script = match data.get("script") {
-            Some(json) => {
-                Some(try_opt!(json.as_u64(),
-                              ErrorStatus::InvalidArgument,
-                              "Script timeout duration was not a signed integer"))
-            }
-            None => None,
-        };
-
-        let page_load = match data.get("pageLoad") {
-            Some(json) => {
-                Some(try_opt!(json.as_u64(),
-                              ErrorStatus::InvalidArgument,
-                              "Page load timeout duration was not a signed integer"))
-            }
-            None => None,
-        };
-
-        let implicit = match data.get("implicit") {
-            Some(json) => {
-                Some(try_opt!(json.as_u64(),
-                              ErrorStatus::InvalidArgument,
-                              "Implicit timeout duration was not a signed integer"))
-            }
-            None => None,
-        };
-
-        Ok(TimeoutsParameters {
-            script: script,
-            page_load: page_load,
-            implicit: implicit,
-        })
-    }
-}
-
-impl ToJson for TimeoutsParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        if let Some(ms) = self.script {
-            data.insert("script".into(), ms.to_json());
+impl<'a> From<&'a TimeoutsParameters> for Value {
+    fn from(params: &'a TimeoutsParameters) -> Value {
+        let mut data = Map::new();
+        if let Some(ms) = params.script {
+            data.insert("script".into(), Value::Number(ms.into()));
         }
-        if let Some(ms) = self.page_load {
-            data.insert("pageLoad".into(), ms.to_json());
+        if let Some(ms) = params.page_load {
+            data.insert("pageLoad".into(), Value::Number(ms.into()));
         }
-        if let Some(ms) = self.implicit {
-            data.insert("implicit".into(), ms.to_json());
+        if let Some(ms) = params.implicit {
+            data.insert("implicit".into(), Value::Number(ms.into()));
         }
-        Json::Object(data)
+        Value::Object(data)
     }
 }
 
 /// A top-level browsing context’s window rect is a dictionary of the
 /// [`screenX`], [`screenY`], `width`, and `height` attributes of the
 /// `WindowProxy`.
 ///
 /// In some user agents the operating system’s window dimensions, including
 /// decorations, are provided by the proprietary `window.outerWidth` and
 /// `window.outerHeight` DOM properties.
 ///
 /// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
 /// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct WindowRectParameters {
-    pub x: Nullable<i32>,
-    pub y: Nullable<i32>,
-    pub width: Nullable<i32>,
-    pub height: Nullable<i32>,
+    pub x: Option<i32>,
+    pub y: Option<i32>,
+    pub width: Option<i32>,
+    pub height: Option<i32>,
 }
 
-impl Parameters for WindowRectParameters {
-    fn from_json(body: &Json) -> WebDriverResult<WindowRectParameters> {
-        let data = try_opt!(body.as_object(),
-            ErrorStatus::InvalidArgument, "Message body was not an object");
-
-        let x = match data.get("x") {
-            Some(json) => try!(Nullable::from_json(json, |n| {
-                let x = try_opt!(
-                    n.as_f64(),
-                    ErrorStatus::InvalidArgument,
-                    "'x' is not a number"
-                ) as i64;
-                if x < i32::min_value() as i64 || x > i32::max_value() as i64 {
-                    return Err(WebDriverError::new(
-                        ErrorStatus::InvalidArgument,
-                        "'x' is larger than i32",
-                    ));
-                }
-                Ok(x as i32)
-            })),
-            None => Nullable::Null,
-        };
-
-        let y = match data.get("y") {
-            Some(json) => try!(Nullable::from_json(json, |n| {
-                let y = try_opt!(
-                    n.as_f64(),
-                    ErrorStatus::InvalidArgument,
-                    "'y' is not a number"
-                ) as i64;
-                if y < i32::min_value() as i64 || y > i32::max_value() as i64 {
-                    return Err(WebDriverError::new(
-                        ErrorStatus::InvalidArgument,
-                        "'y' is larger than i32",
-                    ));
-                }
-                Ok(y as i32)
-            })),
-            None => Nullable::Null,
-        };
-
-        let width = match data.get("width") {
-            Some(json) => try!(Nullable::from_json(json, |n| {
-                let width = try_opt!(
-                    n.as_f64(),
-                    ErrorStatus::InvalidArgument,
-                    "'width' is not a number"
-                ) as i64;
-                if width < 0 || width > i32::max_value() as i64 {
-                    return Err(WebDriverError::new(
-                        ErrorStatus::InvalidArgument,
-                        "'width' is larger than i32",
-                    ));
-                }
-                Ok(width as i32)
-            })),
-            None => Nullable::Null,
-        };
-
-        let height = match data.get("height") {
-            Some(json) => try!(Nullable::from_json(json, |n| {
-                let height = try_opt!(
-                    n.as_f64(),
-                    ErrorStatus::InvalidArgument,
-                    "'height' is not a positive integer"
-                ) as i64;
-                if height < 0 || height > i32::max_value() as i64 {
-                    return Err(WebDriverError::new(
-                        ErrorStatus::InvalidArgument,
-                        "'height' is larger than i32",
-                    ));
-                }
-                Ok(height as i32)
-            })),
-            None => Nullable::Null,
-        };
-
-        Ok(WindowRectParameters { x, y, width, height })
+impl<'a> From<&'a WindowRectParameters> for Value {
+    fn from(params: &'a WindowRectParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("x".to_string(), params.x.map(|x| Value::Number(x.into())).unwrap_or(Value::Null));
+        data.insert("y".to_string(), params.y.map(|x| Value::Number(x.into())).unwrap_or(Value::Null));
+        data.insert("width".to_string(), params.width.map(|x| Value::Number(x.into())).unwrap_or(Value::Null));
+        data.insert("height".to_string(), params.height.map(|x| Value::Number(x.into())).unwrap_or(Value::Null));
+        Value::Object(data)
     }
 }
 
-impl ToJson for WindowRectParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("x".to_string(), self.x.to_json());
-        data.insert("y".to_string(), self.y.to_json());
-        data.insert("width".to_string(), self.width.to_json());
-        data.insert("height".to_string(), self.height.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct SwitchToWindowParameters {
     pub handle: String
 }
 
-impl Parameters for SwitchToWindowParameters {
-    fn from_json(body: &Json) -> WebDriverResult<SwitchToWindowParameters> {
-        let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
-                            "Message body was not an object");
-        let handle = try_opt!(
-            try_opt!(data.get("handle"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'handle' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "'handle' not a string");
-        return Ok(SwitchToWindowParameters {
-            handle: handle.to_string()
-        })
+impl<'a> From<&'a SwitchToWindowParameters> for Value {
+    fn from(params: &'a SwitchToWindowParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("handle".to_string(), params.handle.clone().into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for SwitchToWindowParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("handle".to_string(), self.handle.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct LocatorParameters {
     pub using: LocatorStrategy,
     pub value: String
 }
 
-impl Parameters for LocatorParameters {
-    fn from_json(body: &Json) -> WebDriverResult<LocatorParameters> {
-        let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
-                            "Message body was not an object");
-
-        let using = try!(LocatorStrategy::from_json(
-            try_opt!(data.get("using"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'using' parameter")));
-
-        let value = try_opt!(
-            try_opt!(data.get("value"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'value' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "Could not convert using to string").to_string();
-
-        return Ok(LocatorParameters {
-            using: using,
-            value: value
-        })
+impl<'a> From<&'a LocatorParameters> for Value {
+    fn from(params: &'a LocatorParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("using".to_string(), params.using.into());
+        data.insert("value".to_string(), params.value.clone().into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for LocatorParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("using".to_string(), self.using.to_json());
-        data.insert("value".to_string(), self.value.to_json());
-        Json::Object(data)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SwitchToFrameParameters {
+    pub id: Option<FrameId>
+}
+
+impl<'a> From<&'a SwitchToFrameParameters> for Value {
+    fn from(params: &'a SwitchToFrameParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("id".to_string(), params.id.clone().map(|x| x.into()).unwrap_or(Value::Null));
+        Value::Object(data)
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub struct SwitchToFrameParameters {
-    pub id: FrameId
-}
-
-impl Parameters for SwitchToFrameParameters {
-    fn from_json(body: &Json) -> WebDriverResult<SwitchToFrameParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body was not an object");
-        let id = try!(FrameId::from_json(try_opt!(data.get("id"),
-                                                  ErrorStatus::UnknownError,
-                                                  "Missing 'id' parameter")));
-
-        Ok(SwitchToFrameParameters {
-            id: id
-        })
-    }
-}
-
-impl ToJson for SwitchToFrameParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("id".to_string(), self.id.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct SendKeysParameters {
     pub text: String
 }
 
-impl Parameters for SendKeysParameters {
-    fn from_json(body: &Json) -> WebDriverResult<SendKeysParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Message body was not an object");
-        let text = try_opt!(try_opt!(data.get("text"),
-                                     ErrorStatus::InvalidArgument,
-                                     "Missing 'text' parameter").as_string(),
-                            ErrorStatus::InvalidArgument,
-                            "Could not convert 'text' to string");
-
-        Ok(SendKeysParameters {
-            text: text.into()
-        })
-    }
-}
-
-impl ToJson for SendKeysParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("value".to_string(), self.text.to_json());
-        Json::Object(data)
+impl<'a> From<&'a SendKeysParameters> for Value {
+    fn from(params: &'a SendKeysParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("value".to_string(), params.text.clone().into());
+        Value::Object(data)
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct JavascriptCommandParameters {
     pub script: String,
-    pub args: Nullable<Vec<Json>>
+    pub args: Option<Vec<Value>>
 }
 
-impl Parameters for JavascriptCommandParameters {
-    fn from_json(body: &Json) -> WebDriverResult<JavascriptCommandParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Message body was not an object");
-
-        let args_json = try_opt!(data.get("args"),
-                                 ErrorStatus::InvalidArgument,
-                                 "Missing args parameter");
-
-        let args = try!(Nullable::from_json(
-            args_json,
-            |x| {
-                Ok((try_opt!(x.as_array(),
-                             ErrorStatus::InvalidArgument,
-                             "Failed to convert args to Array")).clone())
-            }));
-
-         //TODO: Look for WebElements in args?
-        let script = try_opt!(
-            try_opt!(data.get("script"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing script parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "Failed to convert script to String");
-        Ok(JavascriptCommandParameters {
-            script: script.to_string(),
-            args: args.clone()
-        })
+impl<'a> From<&'a JavascriptCommandParameters> for Value {
+    fn from(params: &'a JavascriptCommandParameters) -> Value {
+        let mut data = Map::new();
+        //TODO: Wrap script so that it becomes marionette-compatible
+        data.insert("script".to_string(), params.script.clone().into());
+        data.insert("args".to_string(), params.args.clone()
+                    .map(|x| Value::Array(x))
+                    .unwrap_or(Value::Null));
+        Value::Object(data)
     }
 }
 
-impl ToJson for JavascriptCommandParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        //TODO: Wrap script so that it becomes marionette-compatible
-        data.insert("script".to_string(), self.script.to_json());
-        data.insert("args".to_string(), self.args.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct GetNamedCookieParameters {
-    pub name: Nullable<String>,
+    pub name: Option<String>
 }
 
-impl Parameters for GetNamedCookieParameters {
-    fn from_json(body: &Json) -> WebDriverResult<GetNamedCookieParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Message body was not an object");
-        let name_json = try_opt!(data.get("name"),
-                                 ErrorStatus::InvalidArgument,
-                                 "Missing 'name' parameter");
-        let name = try!(Nullable::from_json(name_json, |x| {
-            Ok(try_opt!(x.as_string(),
-                        ErrorStatus::InvalidArgument,
-                        "Failed to convert name to string")
-                .to_string())
-        }));
-        return Ok(GetNamedCookieParameters { name: name });
-    }
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct AddCookieParametersWrapper {
+    cookie: AddCookieParameters
 }
 
-impl ToJson for GetNamedCookieParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("name".to_string(), self.name.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(PartialEq, Serialize, Deserialize, Debug)]
 pub struct AddCookieParameters {
     pub name: String,
     pub value: String,
-    pub path: Nullable<String>,
-    pub domain: Nullable<String>,
-    pub expiry: Nullable<Date>,
+    pub path: Option<String>,
+    pub domain: Option<String>,
+    pub expiry: Option<Date>,
     pub secure: bool,
     pub httpOnly: bool
 }
 
-impl Parameters for AddCookieParameters {
-    fn from_json(body: &Json) -> WebDriverResult<AddCookieParameters> {
-        if !body.is_object() {
-            return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                           "Message body was not an object"));
-        }
-
-        let data = try_opt!(body.find("cookie").and_then(|x| x.as_object()),
-                            ErrorStatus::UnableToSetCookie,
-                            "Cookie parameter not found or not an object");
-
-        let name = try_opt!(
-            try_opt!(data.get("name"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'name' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "'name' is not a string").to_string();
-
-        let value = try_opt!(
-            try_opt!(data.get("value"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'value' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "'value' is not a string").to_string();
-
-        let path = match data.get("path") {
-            Some(path_json) => {
-                try!(Nullable::from_json(
-                    path_json,
-                    |x| {
-                        Ok(try_opt!(x.as_string(),
-                                    ErrorStatus::InvalidArgument,
-                                    "Failed to convert path to String").to_string())
-                    }))
-            },
-            None => Nullable::Null
-        };
-
-        let domain = match data.get("domain") {
-            Some(domain_json) => {
-                try!(Nullable::from_json(
-                    domain_json,
-                    |x| {
-                        Ok(try_opt!(x.as_string(),
-                                    ErrorStatus::InvalidArgument,
-                                    "Failed to convert domain to String").to_string())
-                    }))
-            },
-            None => Nullable::Null
-        };
-
-        let expiry = match data.get("expiry") {
-            Some(expiry_json) => {
-                try!(Nullable::from_json(
-                    expiry_json,
-                    |x| {
-                        Ok(Date::new(try_opt!(x.as_u64(),
-                                              ErrorStatus::InvalidArgument,
-                                              "Failed to convert expiry to Date")))
-                    }))
-            },
-            None => Nullable::Null
-        };
-
-        let secure = match data.get("secure") {
-            Some(x) => try_opt!(x.as_boolean(),
-                                ErrorStatus::InvalidArgument,
-                                "Failed to convert secure to boolean"),
-            None => false
-        };
-
-        let http_only = match data.get("httpOnly") {
-            Some(x) => try_opt!(x.as_boolean(),
-                                ErrorStatus::InvalidArgument,
-                                "Failed to convert httpOnly to boolean"),
-            None => false
-        };
-
-        return Ok(AddCookieParameters {
-            name: name,
-            value: value,
-            path: path,
-            domain: domain,
-            expiry: expiry,
-            secure: secure,
-            httpOnly: http_only
-        })
+impl<'a> From<&'a AddCookieParameters> for Value {
+    fn from(params: &'a AddCookieParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("name".to_string(), params.name.clone().into());
+        data.insert("value".to_string(), params.value.clone().into());
+        data.insert("path".to_string(), params.path.clone().map(|x| x.into()).unwrap_or(Value::Null));
+        data.insert("domain".to_string(), params.domain.clone().map(|x| x.into()).unwrap_or(Value::Null));
+        data.insert("expiry".to_string(), params.expiry.clone().map(|x| x.into()).unwrap_or(Value::Null));
+        data.insert("secure".to_string(), params.secure.into());
+        data.insert("httpOnly".to_string(), params.httpOnly.into());
+        Value::Object(data)
     }
 }
 
-impl ToJson for AddCookieParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("name".to_string(), self.name.to_json());
-        data.insert("value".to_string(), self.value.to_json());
-        data.insert("path".to_string(), self.path.to_json());
-        data.insert("domain".to_string(), self.domain.to_json());
-        data.insert("expiry".to_string(), self.expiry.to_json());
-        data.insert("secure".to_string(), self.secure.to_json());
-        data.insert("httpOnly".to_string(), self.httpOnly.to_json());
-        Json::Object(data)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct TakeScreenshotParameters {
+    pub element: Option<WebElement>
+}
+
+impl<'a> From<&'a TakeScreenshotParameters> for Value {
+    fn from(params: &'a TakeScreenshotParameters) -> Value {
+        let mut data = Map::new();
+        data.insert("element".to_string(), params.element.clone().map(|x| (&x).into()).unwrap_or(Value::Null));
+        Value::Object(data)
     }
 }
 
-#[derive(Debug, PartialEq)]
-pub struct TakeScreenshotParameters {
-    pub element: Nullable<WebElement>
-}
-
-impl Parameters for TakeScreenshotParameters {
-    fn from_json(body: &Json) -> WebDriverResult<TakeScreenshotParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::InvalidArgument,
-                            "Message body was not an object");
-        let element = match data.get("element") {
-            Some(element_json) => try!(Nullable::from_json(
-                element_json,
-                |x| {
-                    Ok(try!(WebElement::from_json(x)))
-                })),
-            None => Nullable::Null
-        };
-
-        return Ok(TakeScreenshotParameters {
-            element: element
-        })
-    }
-}
-
-impl ToJson for TakeScreenshotParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("element".to_string(), self.element.to_json());
-        Json::Object(data)
-    }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct ActionsParameters {
     pub actions: Vec<ActionSequence>
 }
 
-impl Parameters for ActionsParameters {
-    fn from_json(body: &Json) -> WebDriverResult<ActionsParameters> {
-        try_opt!(body.as_object(),
-                 ErrorStatus::InvalidArgument,
-                 "Message body was not an object");
-        let actions = try_opt!(
-            try_opt!(body.find("actions"),
-                     ErrorStatus::InvalidArgument,
-                     "No actions parameter found").as_array(),
-            ErrorStatus::InvalidArgument,
-            "Parameter 'actions' was not an array");
-
-        let mut result = Vec::with_capacity(actions.len());
-        for chain in actions.iter() {
-            result.push(try!(ActionSequence::from_json(chain)));
-        }
-        Ok(ActionsParameters {
-            actions: result
-        })
-    }
-}
-
-impl ToJson for ActionsParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
+impl<'a> From<&'a ActionsParameters> for Value {
+    fn from(params: &'a ActionsParameters) -> Value {
+        let mut data = Map::new();
         data.insert("actions".to_owned(),
-                    self.actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>().to_json());
-        Json::Object(data)
+                    params.actions.iter().map(|x| x.into()).collect::<Vec<Value>>().into());
+        Value::Object(data)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use rustc_serialize::json::Json;
-    use super::{Nullable, Parameters, WindowRectParameters};
+    use serde_json;
+    use super::WindowRectParameters;
 
     #[test]
     fn test_window_rect() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i32),
-            y: Nullable::Value(1i32),
-            width: Nullable::Value(2i32),
-            height: Nullable::Value(3i32),
+            x: Some(0i32),
+            y: Some(1i32),
+            width: Some(2i32),
+            height: Some(3i32),
         };
-        let actual = Json::from_str(r#"{"x": 0, "y": 1, "width": 2, "height": 3}"#).unwrap();
-        assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+        let data = r#"{"x": 0, "y": 1, "width": 2, "height": 3}"#;
+        let actual: WindowRectParameters = serde_json::from_str(&data).unwrap();
+        assert_eq!(expected, actual);
     }
 
     #[test]
     fn test_window_rect_nullable() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i32),
-            y: Nullable::Null,
-            width: Nullable::Value(2i32),
-            height: Nullable::Null,
+            x: Some(0i32),
+            y: None,
+            width: Some(2i32),
+            height: None,
         };
-        let actual = Json::from_str(r#"{"x": 0, "y": null, "width": 2, "height": null}"#).unwrap();
-        assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+        let data = r#"{"x": 0, "y": null, "width": 2, "height": null}"#;
+        let actual: WindowRectParameters = serde_json::from_str(&data).unwrap();
+        assert_eq!(expected, actual);
     }
 
     #[test]
     fn test_window_rect_missing_fields() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i32),
-            y: Nullable::Null,
-            width: Nullable::Value(2i32),
-            height: Nullable::Null,
+            x: Some(0i32),
+            y: None,
+            width: Some(2i32),
+            height: None,
         };
-        let actual = Json::from_str(r#"{"x": 0, "width": 2}"#).unwrap();
-        assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+        let data = r#"{"x": 0, "width": 2}"#;
+        let actual: WindowRectParameters = serde_json::from_str(&data).unwrap();
+        assert_eq!(expected, actual);
     }
 
     #[test]
     fn test_window_rect_floats() {
-        let expected = WindowRectParameters {
-            x: Nullable::Value(1i32),
-            y: Nullable::Value(2i32),
-            width: Nullable::Value(3i32),
-            height: Nullable::Value(4i32),
-        };
-        let actual = Json::from_str(r#"{"x": 1.1, "y": 2.2, "width": 3.3, "height": 4.4}"#).unwrap();
-        assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+        let data = r#"{"x": 1.1, "y": 2.2, "width": 3.3, "height": 4.4}"#;
+        assert!(serde_json::from_str::<WindowRectParameters>(&data).is_err());
     }
 }
--- a/testing/webdriver/src/common.rs
+++ b/testing/webdriver/src/common.rs
@@ -1,222 +1,127 @@
-use rustc_serialize::{Encodable, Encoder};
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
+use serde_json::{Value, Map};
+use std::convert::From;
 
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 
 pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf";
 
-#[derive(Clone, Debug, PartialEq, RustcEncodable)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct Date(pub u64);
 
 impl Date {
     pub fn new(timestamp: u64) -> Date {
         Date(timestamp)
     }
 }
 
-impl ToJson for Date {
-    fn to_json(&self) -> Json {
-        let &Date(x) = self;
-        x.to_json()
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum Nullable<T: ToJson> {
-    Value(T),
-    Null
-}
-
-impl<T: ToJson> Nullable<T> {
-     pub fn is_null(&self) -> bool {
-        match *self {
-            Nullable::Value(_) => false,
-            Nullable::Null => true
-        }
-    }
-
-     pub fn is_value(&self) -> bool {
-        match *self {
-            Nullable::Value(_) => true,
-            Nullable::Null => false
-        }
-    }
-
-    pub fn map<F, U: ToJson>(self, f: F) -> Nullable<U>
-        where F: FnOnce(T) -> U {
-        match self {
-            Nullable::Value(val) => Nullable::Value(f(val)),
-            Nullable::Null => Nullable::Null
-        }
+impl From<Date> for Value {
+    fn from(date: Date) -> Value {
+        let Date(x) = date;
+        x.into()
     }
 }
 
-impl<T: ToJson> Nullable<T> {
-    //This is not very pretty
-    pub fn from_json<F: FnOnce(&Json) -> WebDriverResult<T>>(value: &Json, f: F) -> WebDriverResult<Nullable<T>> {
-        if value.is_null() {
-            Ok(Nullable::Null)
-        } else {
-            Ok(Nullable::Value(try!(f(value))))
-        }
-    }
-}
-
-impl<T: ToJson> ToJson for Nullable<T> {
-    fn to_json(&self) -> Json {
-        match *self {
-            Nullable::Value(ref x) => x.to_json(),
-            Nullable::Null => Json::Null
-        }
-    }
-}
-
-impl<T: ToJson> Encodable for Nullable<T> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        match *self {
-            Nullable::Value(ref x) => x.to_json().encode(s),
-            Nullable::Null => s.emit_option_none()
-        }
-    }
-}
-
-impl<T: ToJson> Into<Option<T>> for Nullable<T> {
-    fn into(self) -> Option<T> {
-        match self {
-            Nullable::Value(val) => Some(val),
-            Nullable::Null => None
-        }
-    }
-}
-
-impl<T: ToJson> From<Option<T>> for Nullable<T> {
-    fn from(option: Option<T>) -> Nullable<T> {
-        match option {
-            Some(val) => Nullable::Value(val),
-            None => Nullable::Null,
-        }
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct WebElement {
+    #[serde(rename="element-6066-11e4-a52e-4f735466cecf")]
     pub id: String
 }
 
 impl WebElement {
     pub fn new(id: String) -> WebElement {
         WebElement {
             id: id
         }
     }
 
-    pub fn from_json(data: &Json) -> WebDriverResult<WebElement> {
+    pub fn from_json(data: &Value) -> WebDriverResult<WebElement> {
         let object = try_opt!(data.as_object(),
                               ErrorStatus::InvalidArgument,
                               "Could not convert webelement to object");
         let id_value = try_opt!(object.get(ELEMENT_KEY),
                                 ErrorStatus::InvalidArgument,
                                 "Could not find webelement key");
 
-        let id = try_opt!(id_value.as_string(),
+        let id = try_opt!(id_value.as_str(),
                           ErrorStatus::InvalidArgument,
                           "Could not convert web element to string").to_string();
 
         Ok(WebElement::new(id))
     }
 }
 
-impl ToJson for WebElement {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert(ELEMENT_KEY.to_string(), self.id.to_json());
-        Json::Object(data)
+impl <'a> From<&'a WebElement> for Value {
+    fn from(elem: &'a WebElement) -> Value {
+        let mut data = Map::new();
+        data.insert(ELEMENT_KEY.to_string(), elem.id.clone().into());
+        Value::Object(data)
     }
 }
 
 impl <T> From<T> for WebElement
     where T: Into<String> {
     fn from(data: T) -> WebElement {
         WebElement::new(data.into())
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub enum FrameId {
     Short(u16),
-    Element(WebElement),
-    Null
+    Element(WebElement)
 }
 
-impl FrameId {
-    pub fn from_json(data: &Json) -> WebDriverResult<FrameId> {
-        match data {
-            &Json::U64(x) => {
-                if x > u16::max_value() as u64 || x < u16::min_value() as u64 {
-                    return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
-                                                   "frame id out of range"))
-                };
-                Ok(FrameId::Short(x as u16))
-            },
-            &Json::Null => Ok(FrameId::Null),
-            &Json::Object(_) => Ok(FrameId::Element(
-                try!(WebElement::from_json(data)))),
-            _ => Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
-                                         "frame id has unexpected type"))
-        }
-    }
-}
-
-impl ToJson for FrameId {
-    fn to_json(&self) -> Json {
-        match *self {
+impl From<FrameId> for Value {
+    fn from(frame_id: FrameId) -> Value {
+        match frame_id {
             FrameId::Short(x) => {
-                Json::U64(x as u64)
+                Value::Number(x.into())
             },
             FrameId::Element(ref x) => {
-                Json::String(x.id.clone())
-            },
-            FrameId::Null => {
-                Json::Null
+                Value::String(x.id.clone())
             }
         }
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub enum LocatorStrategy {
+    #[serde(rename = "css selector")]
     CSSSelector,
+    #[serde(rename = "link text")]
     LinkText,
+    #[serde(rename = "partial link text")]
     PartialLinkText,
+    #[serde(rename = "tag name")]
     TagName,
+    #[serde(rename = "xpath")]
     XPath,
 }
 
 impl LocatorStrategy {
-    pub fn from_json(body: &Json) -> WebDriverResult<LocatorStrategy> {
-        match try_opt!(body.as_string(),
+    pub fn from_json(body: &Value) -> WebDriverResult<LocatorStrategy> {
+        match try_opt!(body.as_str(),
                        ErrorStatus::InvalidArgument,
                        "Expected locator strategy as string") {
             "css selector" => Ok(LocatorStrategy::CSSSelector),
             "link text" => Ok(LocatorStrategy::LinkText),
             "partial link text" => Ok(LocatorStrategy::PartialLinkText),
             "tag name" => Ok(LocatorStrategy::TagName),
             "xpath" => Ok(LocatorStrategy::XPath),
             x => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                          format!("Unknown locator strategy {}", x)))
         }
     }
 }
 
-impl ToJson for LocatorStrategy {
-    fn to_json(&self) -> Json {
-        Json::String(match *self {
+impl From<LocatorStrategy> for Value {
+    fn from(strategy: LocatorStrategy) -> Value {
+        match strategy {
             LocatorStrategy::CSSSelector => "css selector",
             LocatorStrategy::LinkText => "link text",
             LocatorStrategy::PartialLinkText => "partial link text",
             LocatorStrategy::TagName => "tag name",
             LocatorStrategy::XPath => "xpath"
-        }.to_string())
+        }.into()
     }
 }
--- a/testing/webdriver/src/error.rs
+++ b/testing/webdriver/src/error.rs
@@ -1,17 +1,16 @@
 use hyper::status::StatusCode;
-use rustc_serialize::base64::FromBase64Error;
-use rustc_serialize::json::{DecoderError, Json, ParserError, ToJson};
+use base64::DecodeError;
 use std::borrow::Cow;
-use std::collections::BTreeMap;
 use std::convert::From;
 use std::error::Error;
 use std::fmt;
 use std::io;
+use serde_json::{self, Value, Error as SerdeError, Map};
 
 #[derive(Debug, PartialEq)]
 pub enum ErrorStatus {
     /// The [`ElementClick`] command could not be completed because the
     /// [element] receiving the events is obscuring the element that was
     /// requested clicked.
     ///
     /// [`ElementClick`]:
@@ -288,30 +287,30 @@ impl WebDriverError {
         self.error.error_code()
     }
 
     pub fn http_status(&self) -> StatusCode {
         self.error.http_status()
     }
 
     pub fn to_json_string(&self) -> String {
-        self.to_json().to_string()
+        serde_json::to_string(&Value::from(self)).unwrap()
     }
 }
 
-impl ToJson for WebDriverError {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("error".into(), self.error_code().to_json());
-        data.insert("message".into(), self.message.to_json());
-        data.insert("stacktrace".into(), self.stack.to_json());
-
-        let mut wrapper = BTreeMap::new();
-        wrapper.insert("value".into(), Json::Object(data));
-        Json::Object(wrapper)
+impl <'a> From<&'a WebDriverError> for Value {
+    fn from(err: &'a WebDriverError) -> Value {
+        let mut data = Map::new();
+        data.insert("error".into(), err.error_code().into());
+        data.insert("message".into(), err.message.clone().into());
+        data.insert("stacktrace".into(),
+                    format!("{:?}", err.stack).into());
+        let mut wrapper = Map::new();
+        wrapper.insert("value".into(), Value::Object(data));
+        Value::Object(wrapper)
     }
 }
 
 impl Error for WebDriverError {
     fn description(&self) -> &str {
         self.error_code()
     }
 
@@ -321,36 +320,30 @@ impl Error for WebDriverError {
 }
 
 impl fmt::Display for WebDriverError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         self.message.fmt(f)
     }
 }
 
-impl From<ParserError> for WebDriverError {
-    fn from(err: ParserError) -> WebDriverError {
+impl From<SerdeError> for WebDriverError {
+    fn from(err: SerdeError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
 }
 
 impl From<io::Error> for WebDriverError {
     fn from(err: io::Error) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
 }
 
-impl From<DecoderError> for WebDriverError {
-    fn from(err: DecoderError) -> WebDriverError {
-        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
-    }
-}
-
-impl From<FromBase64Error> for WebDriverError {
-    fn from(err: FromBase64Error) -> WebDriverError {
+impl From<DecodeError> for WebDriverError {
+    fn from(err: DecodeError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
 }
 
 impl From<Box<Error>> for WebDriverError {
     fn from(err: Box<Error>) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
--- a/testing/webdriver/src/httpapi.rs
+++ b/testing/webdriver/src/httpapi.rs
@@ -1,13 +1,13 @@
 use regex::{Regex, Captures};
-use rustc_serialize::json::Json;
 
 use hyper::method::Method;
 use hyper::method::Method::{Get, Post, Delete};
+use serde_json::Value;
 
 use command::{WebDriverCommand, WebDriverMessage, WebDriverExtensionCommand,
               VoidWebDriverExtensionCommand};
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 
 fn standard_routes<U:WebDriverExtensionRoute>() -> Vec<(Method, &'static str, Route<U>)> {
     return vec![(Post, "/session", Route::NewSession),
                 (Delete, "/session/{sessionId}", Route::DeleteSession),
@@ -134,26 +134,26 @@ pub enum Route<U:WebDriverExtensionRoute
     TakeElementScreenshot,
     Status,
     Extension(U),
 }
 
 pub trait WebDriverExtensionRoute : Clone + Send + PartialEq {
     type Command: WebDriverExtensionCommand + 'static;
 
-    fn command(&self, &Captures, &Json) -> WebDriverResult<WebDriverCommand<Self::Command>>;
+    fn command(&self, &Captures, &Value) -> WebDriverResult<WebDriverCommand<Self::Command>>;
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct VoidWebDriverExtensionRoute;
 
 impl WebDriverExtensionRoute for VoidWebDriverExtensionRoute {
     type Command = VoidWebDriverExtensionCommand;
 
-    fn command(&self, _:&Captures, _:&Json) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
+    fn command(&self, _:&Captures, _:&Value) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
         panic!("No extensions implemented");
     }
 }
 
 #[derive(Clone, Debug)]
 struct RequestMatcher<U: WebDriverExtensionRoute> {
     method: Method,
     path_regexp: Regex,
--- a/testing/webdriver/src/lib.rs
+++ b/testing/webdriver/src/lib.rs
@@ -1,44 +1,26 @@
 #![allow(non_snake_case)]
 
+extern crate base64;
 #[macro_use]
 extern crate log;
-extern crate rustc_serialize;
 extern crate hyper;
 extern crate regex;
 extern crate cookie;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json;
 extern crate time;
 extern crate url;
 extern crate unicode_segmentation;
 
 #[macro_use] pub mod macros;
 pub mod actions;
 pub mod httpapi;
 pub mod capabilities;
 pub mod command;
 pub mod common;
 pub mod error;
 pub mod server;
 pub mod response;
 
-#[cfg(test)]
-mod nullable_tests {
-    use super::common::Nullable;
-
-    #[test]
-    fn test_nullable_map() {
-        let mut test = Nullable::Value(21);
-
-        assert_eq!(test.map(|x| x << 1), Nullable::Value(42));
-
-        test = Nullable::Null;
-
-        assert_eq!(test.map(|x| x << 1), Nullable::Null);
-    }
-
-    #[test]
-    fn test_nullable_into() {
-        let test: Option<i32> = Nullable::Value(42).into();
-
-        assert_eq!(test, Some(42));
-    }
-}
--- a/testing/webdriver/src/response.rs
+++ b/testing/webdriver/src/response.rs
@@ -1,12 +1,12 @@
-use common::{Date, Nullable};
+use common::Date;
 use cookie;
-use rustc_serialize::json::{self, Json, ToJson};
-use std::collections::BTreeMap;
+use serde_json::{self, Value};
+use std::convert::From;
 use time;
 
 #[derive(Debug)]
 pub enum WebDriverResponse {
     CloseWindow(CloseWindowResponse),
     Cookie(CookieResponse),
     Cookies(CookiesResponse),
     DeleteSession,
@@ -18,107 +18,107 @@ pub enum WebDriverResponse {
     WindowRect(WindowRectResponse),
 }
 
 impl WebDriverResponse {
     pub fn to_json_string(self) -> String {
         use response::WebDriverResponse::*;
 
         let obj = match self {
-            CloseWindow(ref x) => json::encode(&x.to_json()),
-            Cookie(ref x) => json::encode(x),
-            Cookies(ref x) => json::encode(x),
+            CloseWindow(ref x) => serde_json::to_string(&Value::from(x)),
+            Cookie(ref x) => serde_json::to_string(x),
+            Cookies(ref x) => serde_json::to_string(x),
             DeleteSession => Ok("{}".to_string()),
-            ElementRect(ref x) => json::encode(x),
-            Generic(ref x) => json::encode(x),
-            NewSession(ref x) => json::encode(x),
-            Timeouts(ref x) => json::encode(x),
+            ElementRect(ref x) => serde_json::to_string(x),
+            Generic(ref x) => serde_json::to_string(x),
+            NewSession(ref x) => serde_json::to_string(x),
+            Timeouts(ref x) => serde_json::to_string(x),
             Void => Ok("{}".to_string()),
-            WindowRect(ref x) => json::encode(&x.to_json()),
+            WindowRect(ref x) => serde_json::to_string(x),
         }.unwrap();
 
         match self {
             Generic(_) | Cookie(_) | Cookies(_) => obj,
             _ => {
                 let mut data = String::with_capacity(11 + obj.len());
                 data.push_str("{\"value\": ");
                 data.push_str(&*obj);
                 data.push_str("}");
                 data
             }
         }
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct CloseWindowResponse {
     pub window_handles: Vec<String>,
 }
 
 impl CloseWindowResponse {
     pub fn new(handles: Vec<String>) -> CloseWindowResponse {
         CloseWindowResponse { window_handles: handles }
     }
 }
 
-impl ToJson for CloseWindowResponse {
-    fn to_json(&self) -> Json {
-        Json::Array(self.window_handles
+impl <'a> From<&'a CloseWindowResponse> for Value {
+    fn from(resp: &'a CloseWindowResponse) -> Value {
+        Value::Array(resp.window_handles
                     .iter()
-                    .map(|x| Json::String(x.clone()))
-                    .collect::<Vec<Json>>())
+                    .map(|x| Value::String(x.clone()))
+                    .collect::<Vec<Value>>())
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct NewSessionResponse {
     pub sessionId: String,
-    pub capabilities: json::Json
+    pub capabilities: Value
 }
 
 impl NewSessionResponse {
-    pub fn new(session_id: String, capabilities: json::Json) -> NewSessionResponse {
+    pub fn new(session_id: String, capabilities: Value) -> NewSessionResponse {
         NewSessionResponse {
             capabilities: capabilities,
             sessionId: session_id
         }
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct TimeoutsResponse {
     pub script: u64,
     pub pageLoad: u64,
     pub implicit: u64,
 }
 
 impl TimeoutsResponse {
     pub fn new(script: u64, page_load: u64, implicit: u64) -> TimeoutsResponse {
         TimeoutsResponse {
             script: script,
             pageLoad: page_load,
             implicit: implicit,
         }
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct ValueResponse {
-    pub value: json::Json
+    pub value: Value
 }
 
 impl ValueResponse {
-    pub fn new(value: json::Json) -> ValueResponse {
+    pub fn new(value: Value) -> ValueResponse {
         ValueResponse {
             value: value
         }
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct ElementRectResponse {
     /// X axis position of the top-left corner of the element relative
     // to the current browsing context’s document element in CSS reference
     // pixels.
     pub x: f64,
 
     /// Y axis position of the top-left corner of the element relative
     // to the current browsing context’s document element in CSS reference
@@ -133,17 +133,17 @@ pub struct ElementRectResponse {
 
     /// Width of the element’s [bounding rectangle] in CSS reference
     /// pixels.
     ///
     /// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
     pub height: f64,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Serialize)]
 pub struct WindowRectResponse {
     /// `WindowProxy`’s [screenX] attribute.
     ///
     /// [screenX]: https://drafts.csswg.org/cssom-view/#dom-window-screenx
     pub x: i32,
 
     /// `WindowProxy`’s [screenY] attribute.
     ///
@@ -156,121 +156,124 @@ pub struct WindowRectResponse {
     pub width: i32,
 
     /// Height of the top-level browsing context’s outer dimensions, including
     /// any browser chrome and externally drawn window decorations in CSS
     /// reference pixels.
     pub height: i32,
 }
 
-impl ToJson for WindowRectResponse {
-    fn to_json(&self) -> Json {
-        let mut body = BTreeMap::new();
-        body.insert("x".to_owned(), self.x.to_json());
-        body.insert("y".to_owned(), self.y.to_json());
-        body.insert("width".to_owned(), self.width.to_json());
-        body.insert("height".to_owned(), self.height.to_json());
-        Json::Object(body)
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, RustcEncodable)]
+#[derive(Clone, Debug, PartialEq, Serialize)]
 pub struct Cookie {
     pub name: String,
     pub value: String,
-    pub path: Nullable<String>,
-    pub domain: Nullable<String>,
-    pub expiry: Nullable<Date>,
+    pub path: Option<String>,
+    pub domain: Option<String>,
+    pub expiry: Option<Date>,
     pub secure: bool,
     pub httpOnly: bool,
 }
 
+impl Cookie {
+    pub fn new(name: String, value: String, path: Option<String>, domain: Option<String>,
+               expiry: Option<Date>, secure: bool, http_only: bool) -> Cookie {
+        Cookie {
+            name: name,
+            value: value,
+            path: path,
+            domain: domain,
+            expiry: expiry,
+            secure: secure,
+            httpOnly: http_only
+        }
+    }
+}
+
 impl Into<cookie::Cookie<'static>> for Cookie {
     fn into(self) -> cookie::Cookie<'static> {
         let cookie = cookie::Cookie::build(self.name, self.value)
             .secure(self.secure)
             .http_only(self.httpOnly);
         let cookie = match self.domain {
-            Nullable::Value(domain) => cookie.domain(domain),
-            Nullable::Null => cookie,
+            Some(domain) => cookie.domain(domain),
+            None => cookie,
         };
         let cookie = match self.path {
-            Nullable::Value(path) => cookie.path(path),
-            Nullable::Null => cookie,
+            Some(path) => cookie.path(path),
+            None => cookie,
         };
         let cookie = match self.expiry {
-            Nullable::Value(Date(expiry)) => {
+            Some(Date(expiry)) => {
                 cookie.expires(time::at(time::Timespec::new(expiry as i64, 0)))
-            }
-            Nullable::Null => cookie,
+            },
+            None => cookie,
         };
         cookie.finish()
     }
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct CookieResponse {
     pub value: Cookie,
 }
 
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, Serialize)]
 pub struct CookiesResponse {
     pub value: Vec<Cookie>,
 }
 
 #[cfg(test)]
 mod tests {
     use super::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse, ElementRectResponse,
-                NewSessionResponse, Nullable, TimeoutsResponse, ValueResponse, WebDriverResponse,
+                NewSessionResponse, TimeoutsResponse, ValueResponse, WebDriverResponse,
                 WindowRectResponse};
-    use rustc_serialize::json::Json;
-    use std::collections::BTreeMap;
+    use serde_json::{self, Map, Value};
 
     fn test(resp: WebDriverResponse, expected_str: &str) {
         let data = resp.to_json_string();
-        let actual = Json::from_str(&*data).unwrap();
-        let expected = Json::from_str(expected_str).unwrap();
+        let actual: Value = serde_json::from_str(&*data).unwrap();
+        let expected: Value = serde_json::from_str(expected_str).unwrap();
         assert_eq!(actual, expected);
     }
 
     #[test]
     fn test_close_window() {
         let resp = WebDriverResponse::CloseWindow(
             CloseWindowResponse::new(vec!["test".into()]));
         let expected = r#"{"value": ["test"]}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_cookie() {
         let cookie = Cookie {
             name: "name".into(),
             value: "value".into(),
-            path: Nullable::Value("/".into()),
-            domain: Nullable::Null,
-            expiry: Nullable::Null,
+            path: Some("/".into()),
+            domain: None,
+            expiry: None,
             secure: true,
             httpOnly: false,
         };
         let resp = WebDriverResponse::Cookie(CookieResponse { value: cookie });
         let expected = r#"{"value": {"name": "name", "expiry": null, "value": "value",
 "path": "/", "domain": null, "secure": true, "httpOnly": false}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_cookies() {
         let resp = WebDriverResponse::Cookies(CookiesResponse {
             value: vec![
                 Cookie {
                     name: "name".into(),
                     value: "value".into(),
-                    path: Nullable::Value("/".into()),
-                    domain: Nullable::Null,
-                    expiry: Nullable::Null,
+                    path: Some("/".into()),
+                    domain: None,
+                    expiry: None,
                     secure: true,
                     httpOnly: false,
                 }
             ]});
         let expected = r#"{"value": [{"name": "name", "value": "value", "path": "/",
 "domain": null, "expiry": null, "secure": true, "httpOnly": false}]}"#;
         test(resp, expected);
     }
@@ -300,31 +303,31 @@ mod tests {
         let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_new_session() {
         let resp = WebDriverResponse::NewSession(
             NewSessionResponse::new("test".into(),
-                                    Json::Object(BTreeMap::new())));
+                                    Value::Object(Map::new())));
         let expected = r#"{"value": {"sessionId": "test", "capabilities": {}}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_timeouts() {
          let resp = WebDriverResponse::Timeouts(TimeoutsResponse::new(
             1, 2, 3));
         let expected = r#"{"value": {"script": 1, "pageLoad": 2, "implicit": 3}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_value() {
-        let mut value = BTreeMap::new();
-        value.insert("example".into(), Json::Array(vec![Json::String("test".into())]));
+        let mut value = Map::new();
+        value.insert("example".into(), Value::Array(vec![Value::String("test".into())]));
         let resp = WebDriverResponse::Generic(ValueResponse::new(
-            Json::Object(value)));
+            Value::Object(value)));
         let expected = r#"{"value": {"example": ["test"]}}"#;
         test(resp, expected);
     }
 }