Bug 1396821 - [geckodriver] Switch geckodriver crate from rustc_serialize to serde. draft
authorHenrik Skupin <mail@hskupin.info>
Wed, 04 Jul 2018 06:47:04 +0200
changeset 829697 295bd1f706de0fe70892d8b1c5a5c2eb205295a9
parent 829696 064cce12ccd7afc629e959da74dd97b19a8f20fa
child 829698 195c8b10ed2d8319dfe7aa7f5ea5dc35b5d82b3c
push id118789
push userbmo:hskupin@gmail.com
push dateThu, 16 Aug 2018 12:52:34 +0000
bugs1396821
milestone63.0a1
Bug 1396821 - [geckodriver] Switch geckodriver crate from rustc_serialize to serde. MozReview-Commit-ID: B8H3bZQKkJ7
Cargo.lock
testing/geckodriver/Cargo.toml
testing/geckodriver/src/build.rs
testing/geckodriver/src/capabilities.rs
testing/geckodriver/src/main.rs
testing/geckodriver/src/marionette.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -809,26 +809,29 @@ dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckodriver"
 version = "0.21.0"
 dependencies = [
+ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozprofile 0.3.0",
  "mozrunner 0.7.0",
  "mozversion 0.1.3",
  "regex 1.0.0 (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.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
+ "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.36.0",
  "zip 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
--- a/testing/geckodriver/Cargo.toml
+++ b/testing/geckodriver/Cargo.toml
@@ -4,24 +4,27 @@ version = "0.21.0"
 description = "Proxy for using WebDriver clients to interact with Gecko-based browsers."
 keywords = ["webdriver", "w3c", "httpd", "mozilla", "firefox"]
 repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/geckodriver"
 readme = "README.md"
 license = "MPL-2.0"
 publish = false
 
 [dependencies]
+base64 = "0.6"
 chrono = "^0.2"
 clap = { version = "^2.19", default-features = false, features = ["suggestions", "wrap_help"] }
 hyper = "0.10"
 lazy_static = "1.0"
 log = { version = "0.4", features = ["std"] }
 mozprofile = { path = "../mozbase/rust/mozprofile" }
 mozrunner = { path = "../mozbase/rust/mozrunner" }
 mozversion = { path = "../mozbase/rust/mozversion" }
 regex = "1.0"
-rustc-serialize = "0.3"
+serde = "1.0"
+serde_json = "1.0"
+serde_derive = "1.0"
 uuid = { version = "0.5", features = ["v4"] }
 webdriver = { path = "../webdriver" }
 zip = "0.3"
 
 [[bin]]
 name = "geckodriver"
--- a/testing/geckodriver/src/build.rs
+++ b/testing/geckodriver/src/build.rs
@@ -1,12 +1,11 @@
+use serde_json::Value;
 use std::fmt;
 
-use rustc_serialize::json::{Json};
-
 include!(concat!(env!("OUT_DIR"), "/build-info.rs"));
 
 pub struct BuildInfo;
 
 impl BuildInfo {
     pub fn version() -> &'static str {
         crate_version!()
     }
@@ -27,15 +26,15 @@ impl fmt::Display for BuildInfo {
             (Some(hash), Some(date)) => write!(f, " ({} {})", hash, date)?,
             (Some(hash), None) => write!(f, " ({})", hash)?,
             _ => {}
         }
         Ok(())
     }
 }
 
+// TODO(Henrik): Change into From
 //std::convert::From<&str>` is not implemented for `rustc_serialize::json::Json
-
-impl Into<Json> for BuildInfo {
-    fn into(self) -> Json {
-        Json::String(BuildInfo::version().to_string())
+impl Into<Value> for BuildInfo {
+    fn into(self) -> Value {
+        Value::String(BuildInfo::version().to_string())
     }
 }
--- a/testing/geckodriver/src/capabilities.rs
+++ b/testing/geckodriver/src/capabilities.rs
@@ -1,17 +1,17 @@
+use base64;
 use logging::Level;
 use marionette::LogOptions;
 use mozprofile::preferences::Pref;
 use mozprofile::profile::Profile;
 use mozrunner::runner::platform::firefox_default_path;
 use mozversion::{self, firefox_version, Version};
 use regex::bytes::Regex;
-use rustc_serialize::base64::FromBase64;
-use rustc_serialize::json::Json;
+use serde_json::{Map, Value};
 use std::collections::BTreeMap;
 use std::default::Default;
 use std::error::Error;
 use std::fs;
 use std::io::BufWriter;
 use std::io::Cursor;
 use std::io;
 use std::path::{Path, PathBuf};
@@ -39,21 +39,21 @@ impl<'a> FirefoxCapabilities<'a> {
     pub fn new(fallback_binary: Option<&'a PathBuf>) -> FirefoxCapabilities<'a> {
         FirefoxCapabilities {
             chosen_binary: None,
             fallback_binary: fallback_binary,
             version_cache: BTreeMap::new(),
         }
     }
 
-    fn set_binary(&mut self, capabilities: &BTreeMap<String, Json>) {
+    fn set_binary(&mut self, capabilities: &Map<String, Value>) {
         self.chosen_binary = capabilities
             .get("moz:firefoxOptions")
-            .and_then(|x| x.find("binary"))
-            .and_then(|x| x.as_string())
+            .and_then(|x| x.get("binary"))
+            .and_then(|x| x.as_str())
             .map(|x| PathBuf::from(x))
             .or_else(|| self.fallback_binary.map(|x| x.clone()))
             .or_else(|| firefox_default_path())
     }
 
     fn version(&mut self) -> Option<String> {
         if let Some(ref binary) = self.chosen_binary {
             if let Some(value) = self.version_cache.get(binary) {
@@ -153,17 +153,17 @@ impl<'a> BrowserCapabilities for Firefox
             .matches(comparison)
             .or_else(|x| Err(convert_version_error(x)))
     }
 
     fn accept_proxy(&mut self, _: &Capabilities, _: &Capabilities) -> WebDriverResult<bool> {
         Ok(true)
     }
 
-    fn validate_custom(&self, name: &str,  value: &Json) -> WebDriverResult<()> {
+    fn validate_custom(&self, name: &str,  value: &Value) -> WebDriverResult<()> {
         if !name.starts_with("moz:") {
             return Ok(())
         }
         match name {
             "moz:firefoxOptions" => {
                 let data = try_opt!(value.as_object(),
                                     ErrorStatus::InvalidArgument,
                                     "moz:firefoxOptions is not an object");
@@ -196,17 +196,17 @@ impl<'a> BrowserCapabilities for Firefox
                         },
                         "log" => {
                             let log_data = try_opt!(value.as_object(),
                                                     ErrorStatus::InvalidArgument,
                                                     "log value is not an object");
                             for (log_key, log_value) in log_data.iter() {
                                 match &**log_key {
                                     "level" => {
-                                        let level = try_opt!(log_value.as_string(),
+                                        let level = try_opt!(log_value.as_str(),
                                                              ErrorStatus::InvalidArgument,
                                                              "log level is not a string");
                                         if Level::from_str(level).is_err() {
                                             return Err(WebDriverError::new(
                                                 ErrorStatus::InvalidArgument,
                                                 format!("Not a valid log level: {}", level)))
                                         }
                                     }
@@ -248,28 +248,28 @@ impl<'a> BrowserCapabilities for Firefox
                 }
             }
             _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                 format!("Unrecognised option {}", name)))
         }
         Ok(())
     }
 
-    fn accept_custom(&mut self, _: &str, _: &Json, _: &Capabilities) -> WebDriverResult<bool> {
+    fn accept_custom(&mut self, _: &str, _: &Value, _: &Capabilities) -> WebDriverResult<bool> {
         Ok(true)
     }
 }
 
 /// Rust representation of `moz:firefoxOptions`.
 ///
 /// Calling `FirefoxOptions::from_capabilities(binary, capabilities)` causes
 /// the encoded profile, the binary arguments, log settings, and additional
 /// preferences to be checked and unmarshaled from the `moz:firefoxOptions`
 /// JSON Object into a Rust representation.
-#[derive(Default)]
+#[derive(Default, Debug)]
 pub struct FirefoxOptions {
     pub binary: Option<PathBuf>,
     pub profile: Option<Profile>,
     pub args: Option<Vec<String>>,
     pub log: LogOptions,
     pub prefs: Vec<(String, Pref)>,
 }
 
@@ -298,20 +298,20 @@ impl FirefoxOptions {
 
         Ok(rv)
     }
 
     fn load_profile(options: &Capabilities) -> WebDriverResult<Option<Profile>> {
         if let Some(profile_json) = options.get("profile") {
             let profile_base64 =
                 try!(profile_json
-                         .as_string()
+                         .as_str()
                          .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
                                                     "Profile is not a string")));
-            let profile_zip = &*try!(profile_base64.from_base64());
+            let profile_zip = &*try!(base64::decode(profile_base64));
 
             // Create an emtpy profile directory
             let profile = try!(Profile::new(None));
             try!(unzip_buffer(profile_zip,
                               profile
                                   .temp_dir
                                   .as_ref()
                                   .expect("Profile doesn't have a path")
@@ -327,17 +327,17 @@ impl FirefoxOptions {
         if let Some(args_json) = options.get("args") {
             let args_array = try!(args_json
                                       .as_array()
                                       .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
                                                                  "Arguments were not an \
                                                                   array")));
             let args = try!(args_array
                                 .iter()
-                                .map(|x| x.as_string().map(|x| x.to_owned()))
+                                .map(|x| x.as_str().map(|x| x.to_owned()))
                                 .collect::<Option<Vec<String>>>()
                                 .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
                                                            "Arguments entries were not all \
                                                             strings")));
             Ok(Some(args))
         } else {
             Ok(None)
         }
@@ -347,17 +347,17 @@ impl FirefoxOptions {
         if let Some(json) = options.get("log") {
             let log = json.as_object().ok_or(WebDriverError::new(
                 ErrorStatus::InvalidArgument,
                 "Log section is not an object",
             ))?;
 
             let level = match log.get("level") {
                 Some(json) => {
-                    let s = json.as_string().ok_or(WebDriverError::new(
+                    let s = json.as_str().ok_or(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "Log level is not a string",
                     ))?;
                     Some(Level::from_str(s).ok().ok_or(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "Log level is unknown",
                     ))?)
                 }
@@ -382,22 +382,21 @@ impl FirefoxOptions {
             }
             Ok(rv)
         } else {
             Ok(vec![])
         }
     }
 }
 
-fn pref_from_json(value: &Json) -> WebDriverResult<Pref> {
+fn pref_from_json(value: &Value) -> WebDriverResult<Pref> {
     match value {
-        &Json::String(ref x) => Ok(Pref::new(x.clone())),
-        &Json::I64(x) => Ok(Pref::new(x)),
-        &Json::U64(x) => Ok(Pref::new(x as i64)),
-        &Json::Boolean(x) => Ok(Pref::new(x)),
+        &Value::String(ref x) => Ok(Pref::new(x.clone())),
+        &Value::Number(ref x) => Ok(Pref::new(x.as_i64().unwrap())),
+        &Value::Bool(x) => Ok(Pref::new(x)),
         _ => Err(WebDriverError::new(ErrorStatus::UnknownError,
                                      "Could not convert pref value to string, boolean, or integer"))
     }
 }
 
 fn unzip_buffer(buf: &[u8], dest_dir: &Path) -> WebDriverResult<()> {
     let reader = Cursor::new(buf);
     let mut zip = try!(zip::ZipArchive::new(reader).map_err(|_| {
@@ -446,46 +445,36 @@ fn unzip_buffer(buf: &[u8], dest_dir: &P
     }
 
     Ok(())
 }
 
 #[cfg(test)]
 mod tests {
     extern crate mozprofile;
-    extern crate rustc_serialize;
 
     use self::mozprofile::preferences::Pref;
-    use self::rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
-    use self::rustc_serialize::json::Json;
-    use super::FirefoxOptions;
+    use super::*;
     use marionette::MarionetteHandler;
-    use std::collections::BTreeMap;
     use std::default::Default;
     use std::fs::File;
     use std::io::Read;
 
     use webdriver::capabilities::Capabilities;
 
-    fn example_profile() -> Json {
+    fn example_profile() -> Value {
         let mut profile_data = Vec::with_capacity(1024);
         let mut profile = File::open("src/tests/profile.zip").unwrap();
         profile.read_to_end(&mut profile_data).unwrap();
-        let base64_config = Config {
-            char_set: CharacterSet::Standard,
-            newline: Newline::LF,
-            pad: true,
-            line_length: None,
-        };
-        Json::String(profile_data.to_base64(base64_config))
+        Value::String(base64::encode(&profile_data))
     }
 
     fn make_options(firefox_opts: Capabilities) -> FirefoxOptions {
         let mut caps = Capabilities::new();
-        caps.insert("moz:firefoxOptions".into(), Json::Object(firefox_opts));
+        caps.insert("moz:firefoxOptions".into(), Value::Object(firefox_opts));
         let binary = None;
         FirefoxOptions::from_capabilities(binary, &mut caps).unwrap()
     }
 
     #[test]
     fn test_profile() {
         let encoded_profile = example_profile();
         let mut firefox_opts = Capabilities::new();
@@ -499,25 +488,25 @@ mod tests {
 
         assert_eq!(prefs.get("startup.homepage_welcome_url"),
                    Some(&Pref::new("data:text/html,PASS")));
     }
 
     #[test]
     fn test_prefs() {
         let encoded_profile = example_profile();
-        let mut prefs: BTreeMap<String, Json> = BTreeMap::new();
+        let mut prefs: Map<String, Value> = Map::new();
         prefs.insert(
             "browser.display.background_color".into(),
-            Json::String("#00ff00".into()),
+            Value::String("#00ff00".into()),
         );
 
         let mut firefox_opts = Capabilities::new();
         firefox_opts.insert("profile".into(), encoded_profile);
-        firefox_opts.insert("prefs".into(), Json::Object(prefs));
+        firefox_opts.insert("prefs".into(), Value::Object(prefs));
 
         let opts = make_options(firefox_opts);
         let mut profile = opts.profile.unwrap();
 
         let handler = MarionetteHandler::new(Default::default());
         handler
             .set_prefs(2828, &mut profile, true, opts.prefs)
             .unwrap();
--- a/testing/geckodriver/src/main.rs
+++ b/testing/geckodriver/src/main.rs
@@ -1,19 +1,23 @@
+extern crate base64;
 extern crate chrono;
 #[macro_use]
 extern crate clap;
 #[macro_use]
 extern crate lazy_static;
 extern crate hyper;
 extern crate mozprofile;
 extern crate mozrunner;
 extern crate mozversion;
 extern crate regex;
-extern crate rustc_serialize;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json;
 extern crate uuid;
 extern crate zip;
 extern crate webdriver;
 
 #[macro_use]
 extern crate log;
 
 use std::io::Write;
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -1,33 +1,32 @@
+use base64;
 use hyper::method::Method;
 use mozprofile::preferences::Pref;
 use mozprofile::profile::Profile;
 use mozrunner::runner::{FirefoxRunner, FirefoxProcess, Runner, RunnerProcess};
 use regex::Captures;
-use rustc_serialize::base64::FromBase64;
-use rustc_serialize::json;
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
+use serde::de::{self, Deserialize, Deserializer};
+use serde::ser::{Serialize, Serializer};
+use serde_json::{self, Map, Value};
 use std::env;
 use std::error::Error;
 use std::fs::File;
 use std::io::Error as IoError;
 use std::io::ErrorKind;
 use std::io::prelude::*;
 use std::path::PathBuf;
 use std::io::Result as IoResult;
 use std::net::{TcpListener, TcpStream};
 use std::sync::Mutex;
 use std::thread;
 use std::time;
 use uuid::Uuid;
 use webdriver::capabilities::CapabilitiesMatching;
-use webdriver::command::{WebDriverCommand, WebDriverMessage, Parameters,
-                         WebDriverExtensionCommand};
+use webdriver::command::{WebDriverCommand, WebDriverMessage, WebDriverExtensionCommand};
 use webdriver::command::WebDriverCommand::{
     NewSession, DeleteSession, Status, Get, GetCurrentUrl,
     GoBack, GoForward, Refresh, GetTitle, GetPageSource, GetWindowHandle,
     GetWindowHandles, CloseWindow, SetWindowRect, GetWindowRect,
     MinimizeWindow, MaximizeWindow, FullscreenWindow, SwitchToWindow, SwitchToFrame,
     SwitchToParentFrame, FindElement, FindElements,
     FindElementElement, FindElementElements, GetActiveElement,
     IsDisplayed, IsSelected, GetElementAttribute, GetElementProperty, GetCSSValue,
@@ -37,20 +36,20 @@ use webdriver::command::WebDriverCommand
     DeleteCookies, DeleteCookie, GetTimeouts, SetTimeouts, DismissAlert,
     AcceptAlert, GetAlertText, SendAlertText, TakeScreenshot, TakeElementScreenshot,
     Extension, PerformActions, ReleaseActions};
 use webdriver::command::{
     NewSessionParameters, GetParameters, WindowRectParameters, SwitchToWindowParameters,
     SwitchToFrameParameters, LocatorParameters, JavascriptCommandParameters,
     GetNamedCookieParameters, AddCookieParameters, TimeoutsParameters,
     ActionsParameters, TakeScreenshotParameters};
-use webdriver::response::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse,
+use webdriver::response::{CloseWindowResponse, CookieResponse, CookiesResponse,
                           ElementRectResponse, NewSessionResponse, TimeoutsResponse,
                           ValueResponse, WebDriverResponse, WindowRectResponse};
-use webdriver::common::{Date, ELEMENT_KEY, FrameId, FRAME_KEY, Nullable, WebElement, WINDOW_KEY};
+use webdriver::common::{Cookie, ELEMENT_KEY, FrameId, FRAME_KEY, WebElement, WINDOW_KEY};
 use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
 use webdriver::server::{WebDriverHandler, Session};
 use webdriver::httpapi::{WebDriverExtensionRoute};
 
 use build::BuildInfo;
 use capabilities::{FirefoxCapabilities, FirefoxOptions};
 use logging;
 use prefs;
@@ -87,45 +86,43 @@ pub enum GeckoExtensionRoute {
     UninstallAddon,
 }
 
 impl WebDriverExtensionRoute for GeckoExtensionRoute {
     type Command = GeckoExtensionCommand;
 
     fn command(&self,
                captures: &Captures,
-               body_data: &Json)
+               body_data: &Value)
                -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> {
         let command = match self {
             &GeckoExtensionRoute::GetContext => GeckoExtensionCommand::GetContext,
             &GeckoExtensionRoute::SetContext => {
-                let parameters: GeckoContextParameters = try!(Parameters::from_json(&body_data));
-                GeckoExtensionCommand::SetContext(parameters)
+                GeckoExtensionCommand::SetContext(serde_json::from_value(body_data.clone())?)
             }
             &GeckoExtensionRoute::XblAnonymousChildren => {
                 let element_id = try!(captures.name("elementId")
                     .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                "Missing elementId parameter")));
-                GeckoExtensionCommand::XblAnonymousChildren(element_id.as_str().into())
+                GeckoExtensionCommand::XblAnonymousChildren(
+                    WebElement::new(element_id.as_str().to_string()))
             }
             &GeckoExtensionRoute::XblAnonymousByAttribute => {
                 let element_id = try!(captures.name("elementId")
                     .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                "Missing elementId parameter")));
-                let parameters: AttributeParameters = try!(Parameters::from_json(&body_data));
-                GeckoExtensionCommand::XblAnonymousByAttribute(element_id.as_str().into(),
-                                                               parameters)
+                GeckoExtensionCommand::XblAnonymousByAttribute(
+                    WebElement::new(element_id.as_str().to_string()),
+                    serde_json::from_value(body_data.clone())?)
             }
             &GeckoExtensionRoute::InstallAddon => {
-                let parameters: AddonInstallParameters = try!(Parameters::from_json(&body_data));
-                GeckoExtensionCommand::InstallAddon(parameters)
+                GeckoExtensionCommand::InstallAddon(serde_json::from_value(body_data.clone())?)
             }
             &GeckoExtensionRoute::UninstallAddon => {
-                let parameters: AddonUninstallParameters = try!(Parameters::from_json(&body_data));
-                GeckoExtensionCommand::UninstallAddon(parameters)
+                GeckoExtensionCommand::UninstallAddon(serde_json::from_value(body_data.clone())?)
             }
         };
         Ok(WebDriverCommand::Extension(command))
     }
 }
 
 #[derive(Clone, PartialEq)]
 pub enum GeckoExtensionCommand {
@@ -133,266 +130,166 @@ pub enum GeckoExtensionCommand {
     SetContext(GeckoContextParameters),
     XblAnonymousChildren(WebElement),
     XblAnonymousByAttribute(WebElement, AttributeParameters),
     InstallAddon(AddonInstallParameters),
     UninstallAddon(AddonUninstallParameters)
 }
 
 impl WebDriverExtensionCommand for GeckoExtensionCommand {
-    fn parameters_json(&self) -> Option<Json> {
+    fn parameters_json(&self) -> Option<Value> {
         match self {
+            // TODO: Remove unwrap
             &GeckoExtensionCommand::GetContext => None,
-            &GeckoExtensionCommand::SetContext(ref x) => Some(x.to_json()),
+            &GeckoExtensionCommand::SetContext(ref x) => Some(serde_json::to_value(x.clone()).unwrap()),
             &GeckoExtensionCommand::XblAnonymousChildren(_) => None,
-            &GeckoExtensionCommand::XblAnonymousByAttribute(_, ref x) => Some(x.to_json()),
-            &GeckoExtensionCommand::InstallAddon(ref x) => Some(x.to_json()),
-            &GeckoExtensionCommand::UninstallAddon(ref x) => Some(x.to_json()),
-        }
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-enum GeckoContext {
-    Content,
-    Chrome,
-}
-
-impl ToJson for GeckoContext {
-    fn to_json(&self) -> Json {
-        match self {
-            &GeckoContext::Content => Json::String("content".to_owned()),
-            &GeckoContext::Chrome => Json::String("chrome".to_owned()),
+            &GeckoExtensionCommand::XblAnonymousByAttribute(_, ref x) => Some(serde_json::to_value(x.clone()).unwrap()),
+            &GeckoExtensionCommand::InstallAddon(ref x) => Some(serde_json::to_value(x.clone()).unwrap()),
+            &GeckoExtensionCommand::UninstallAddon(ref x) => Some(serde_json::to_value(x.clone()).unwrap()),
         }
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
-pub struct GeckoContextParameters {
-    context: GeckoContext
-}
-
-impl Parameters for GeckoContextParameters {
-    fn from_json(body: &Json) -> WebDriverResult<GeckoContextParameters> {
-        let data = try!(body.as_object().ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Message body was not an object")));
-        let context_value = try!(data.get("context").ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Missing context key")));
-        let value = try!(context_value.as_string().ok_or(
-            WebDriverError::new(
-                ErrorStatus::InvalidArgument,
-                "context was not a string")));
-        let context = try!(match value {
-            "chrome" => Ok(GeckoContext::Chrome),
-            "content" => Ok(GeckoContext::Content),
-            _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                         format!("{} is not a valid context",
-                                                 value)))
-        });
-        Ok(GeckoContextParameters {
-            context: context
-        })
-    }
-}
-
-impl ToMarionette for GeckoContextParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("value".to_owned(), self.context.to_json());
-        Ok(data)
-    }
-}
-
-impl ToJson for GeckoContextParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("context".to_owned(), self.context.to_json());
-        Json::Object(data)
-    }
-}
-
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct AttributeParameters {
     name: String,
     value: String
 }
 
-impl Parameters for AttributeParameters {
-    fn from_json(body: &Json) -> WebDriverResult<AttributeParameters> {
-        let data = try!(body.as_object().ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Message body was not an object")));
-        let name = try!(try!(data.get("name").ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Missing 'name' parameter"))).as_string().
-                            ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                      "'name' parameter is not a string")));
-        let value = try!(try!(data.get("value").ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Missing 'value' parameter"))).as_string().
-                            ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                      "'value' parameter is not a string")));
-        Ok(AttributeParameters {
-            name: name.to_owned(),
-            value: value.to_owned(),
-        })
-    }
-}
-
-impl ToJson for AttributeParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("name".to_owned(), self.name.to_json());
-        data.insert("value".to_owned(), self.value.to_json());
-        Json::Object(data)
-    }
-}
-
 impl ToMarionette for AttributeParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("using".to_owned(), "anon attribute".to_json());
-        let mut value = BTreeMap::new();
-        value.insert(self.name.to_owned(), self.value.to_json());
-        data.insert("value".to_owned(), Json::Object(value));
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("using".to_owned(), Value::String("anon attribute".to_string()));
+        let mut value = Map::new();
+        value.insert(self.name.to_owned(), Value::String(self.value.clone()));
+        data.insert("value".to_owned(), Value::Object(value));
         Ok(data)
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize)]
 pub struct AddonInstallParameters {
     pub path: String,
     pub temporary: bool
 }
 
-impl Parameters for AddonInstallParameters {
-    fn from_json(body: &Json) -> WebDriverResult<AddonInstallParameters> {
-        let data = try!(body.as_object().ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Message body was not an object")));
+impl<'de> Deserialize<'de> for AddonInstallParameters {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Debug, Deserialize)]
+        #[serde(deny_unknown_fields)]
+        struct Base64 { addon: String, temporary: bool };
 
-        let base64 = match data.get("addon") {
-            Some(x) => {
-                let s = try_opt!(x.as_string(),
-                                 ErrorStatus::InvalidArgument,
-                                 "'addon' is not a string").to_string();
+        #[derive(Debug, Deserialize)]
+        #[serde(deny_unknown_fields)]
+        struct Path { path: String, temporary: bool };
 
-                let addon_path = env::temp_dir().as_path()
-                    .join(format!("addon-{}.xpi", Uuid::new_v4()));
-                let mut addon_file = try!(File::create(&addon_path));
-                let addon_buf = try!(s.from_base64());
-                try!(addon_file.write(addon_buf.as_slice()));
+        #[derive(Debug, Deserialize)]
+        #[serde(untagged)]
+        enum Helper { Base64(Base64), Path(Path) };
 
-                Some(try_opt!(addon_path.to_str(),
-                              ErrorStatus::UnknownError,
-                              "could not write addon to file").to_string())
+        let params = match Helper::deserialize(deserializer)? {
+            Helper::Path(ref mut data) => {
+                AddonInstallParameters {
+                    path: data.path.clone(),
+                    temporary: data.temporary,
+                }
             },
-            None => None,
-        };
-        let path = match data.get("path") {
-            Some(x) => Some(try_opt!(x.as_string(),
-                                     ErrorStatus::InvalidArgument,
-                                     "'path' is not a string").to_string()),
-            None => None,
-        };
-        if (base64.is_none() && path.is_none()) || (base64.is_some() && path.is_some()) {
-            return Err(WebDriverError::new(
-                ErrorStatus::InvalidArgument,
-                "Must specify exactly one of 'path' and 'addon'"));
-        }
+            Helper::Base64(ref mut data) => {
+                let content = base64::decode(&data.addon).map_err(de::Error::custom)?;
+
+                let path = env::temp_dir().as_path().join(format!("addon-{}.xpi", Uuid::new_v4()));
+                let mut xpi_file = File::create(&path).map_err(de::Error::custom)?;
+                xpi_file.write(content.as_slice()).map_err(de::Error::custom)?;
 
-        let temporary = match data.get("temporary") {
-            Some(x) => try_opt!(x.as_boolean(),
-                                ErrorStatus::InvalidArgument,
-                                "Failed to convert 'temporary' to boolean"),
-            None => false
+                let path = match path.to_str() {
+                    Some(path) => path.to_string(),
+                    None => return Err(de::Error::custom("could not write addon to file"))
+                };
+
+                AddonInstallParameters {
+                    path: path,
+                    temporary: data.temporary,
+                }
+            }
         };
 
-        return Ok(AddonInstallParameters {
-            path: base64.or(path).unwrap(),
-            temporary: temporary,
-        })
-    }
-}
-
-impl ToJson for AddonInstallParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("path".to_string(), self.path.to_json());
-        data.insert("temporary".to_string(), self.temporary.to_json());
-        Json::Object(data)
+        Ok(params)
     }
 }
 
 impl ToMarionette for AddonInstallParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("path".to_string(), self.path.to_json());
-        data.insert("temporary".to_string(), self.temporary.to_json());
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("path".to_string(), Value::String(self.path.clone()));
+        data.insert("temporary".to_string(), Value::Bool(self.temporary));
         Ok(data)
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct AddonUninstallParameters {
     pub id: String
 }
 
-impl Parameters for AddonUninstallParameters {
-    fn from_json(body: &Json) -> WebDriverResult<AddonUninstallParameters> {
-        let data = try!(body.as_object().ok_or(
-            WebDriverError::new(ErrorStatus::InvalidArgument,
-                                "Message body was not an object")));
-
-        let id = try_opt!(
-            try_opt!(data.get("id"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'id' parameter").as_string(),
-            ErrorStatus::InvalidArgument,
-            "'id' is not a string").to_string();
-
-        return Ok(AddonUninstallParameters {id: id})
-    }
-}
-
-impl ToJson for AddonUninstallParameters {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("id".to_string(), self.id.to_json());
-        Json::Object(data)
-    }
-}
-
 impl ToMarionette for AddonUninstallParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("id".to_string(), self.id.to_json());
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("id".to_string(), Value::String(self.id.clone()));
         Ok(data)
     }
 }
 
-#[derive(Default)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+enum GeckoContext {
+    Content,
+    Chrome,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct GeckoContextParameters {
+    context: GeckoContext
+}
+
+impl ToMarionette for GeckoContextParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("value".to_owned(), serde_json::to_value(self.context.clone())?);
+        Ok(data)
+    }
+}
+
+#[derive(Default, Debug)]
 pub struct LogOptions {
     pub level: Option<logging::Level>,
 }
 
+#[derive(Debug, PartialEq, Deserialize)]
+pub struct MarionetteHandshake {
+    #[serde(rename = "marionetteProtocol")]
+    protocol: u16,
+    #[serde(rename = "applicationType")]
+    application_type: String,
+}
+
 #[derive(Default)]
 pub struct MarionetteSettings {
     pub port: Option<u16>,
     pub binary: Option<PathBuf>,
     pub connect_existing: bool,
 
     /// Brings up the Browser Toolbox when starting Firefox,
     /// letting you debug internals.
     pub jsdebugger: bool,
 }
 
+#[derive(Default)]
 pub struct MarionetteHandler {
     connection: Mutex<Option<MarionetteConnection>>,
     settings: MarionetteSettings,
     browser: Option<FirefoxProcess>,
 }
 
 impl MarionetteHandler {
     pub fn new(settings: MarionetteSettings) -> MarionetteHandler {
@@ -401,17 +298,17 @@ impl MarionetteHandler {
             settings,
             browser: None,
         }
     }
 
     fn create_connection(&mut self,
                          session_id: &Option<String>,
                          new_session_parameters: &NewSessionParameters)
-                         -> WebDriverResult<BTreeMap<String, Json>> {
+                         -> WebDriverResult<Map<String, Value>> {
         let (options, capabilities) = {
             let mut fx_capabilities = FirefoxCapabilities::new(self.settings.binary.as_ref());
             let mut capabilities = try!(
                 try!(new_session_parameters
                     .match_browser(&mut fx_capabilities))
                     .ok_or(WebDriverError::new(
                         ErrorStatus::SessionNotCreated,
                         "Unable to find a matching set of capabilities")));
@@ -426,19 +323,18 @@ impl MarionetteHandler {
         }
 
         let port = self.settings.port.unwrap_or(get_free_port()?);
         if !self.settings.connect_existing {
             try!(self.start_browser(port, options));
         }
 
         let mut connection = MarionetteConnection::new(port, session_id.clone());
-        try!(connection.connect(&mut self.browser));
+        connection.connect(&mut self.browser)?;
         self.connection = Mutex::new(Some(connection));
-
         Ok(capabilities)
     }
 
     fn start_browser(&mut self, port: u16, options: FirefoxOptions) -> WebDriverResult<()> {
         let binary = options.binary
             .ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated,
                                        "Expected browser binary location, but unable to find \
                                         binary in default location, no \
@@ -527,21 +423,22 @@ impl WebDriverHandler<GeckoExtensionRout
             // connection or message
             if msg.command == Status {
                 let (ready, message) = self.connection.lock()
                     .map(|ref connection| connection
                          .as_ref()
                          .map(|_| (false, "Session already started"))
                          .unwrap_or((true, "")))
                     .unwrap_or((false, "geckodriver internal error"));
-                let mut value = BTreeMap::new();
-                value.insert("ready".to_string(), Json::Boolean(ready));
-                value.insert("message".to_string(), Json::String(message.into()));
-                return Ok(WebDriverResponse::Generic(ValueResponse::new(Json::Object(value))));
+                let mut value = Map::new();
+                value.insert("ready".to_string(), Value::Bool(ready));
+                value.insert("message".to_string(), Value::String(message.into()));
+                return Ok(WebDriverResponse::Generic(ValueResponse::new(Value::Object(value))));
             }
+
             match self.connection.lock() {
                 Ok(ref connection) => {
                     if connection.is_none() {
                         match msg.command {
                             NewSession(ref capabilities) => {
                                 capabilities_options = Some(capabilities);
                             },
                             _ => {
@@ -614,17 +511,17 @@ impl WebDriverHandler<GeckoExtensionRout
 
         self.connection = Mutex::new(None);
         self.browser = None;
     }
 }
 
 pub struct MarionetteSession {
     pub session_id: String,
-    protocol: Option<String>,
+    protocol: Option<u16>,
     application_type: Option<String>,
     command_id: u64
 }
 
 impl MarionetteSession {
     pub fn new(session_id: Option<String>) -> MarionetteSession {
         let initital_id = session_id.unwrap_or("".to_string());
         MarionetteSession {
@@ -635,33 +532,33 @@ impl MarionetteSession {
         }
     }
 
     pub fn update(&mut self, msg: &WebDriverMessage<GeckoExtensionRoute>,
                   resp: &MarionetteResponse) -> WebDriverResult<()> {
         match msg.command {
             NewSession(_) => {
                 let session_id = try_opt!(
-                    try_opt!(resp.result.find("sessionId"),
+                    try_opt!(resp.result.get("sessionId"),
                              ErrorStatus::SessionNotCreated,
-                             "Unable to get session id").as_string(),
+                             "Unable to get session id").as_str(),
                         ErrorStatus::SessionNotCreated,
                         "Unable to convert session id to string");
                 self.session_id = session_id.to_string().clone();
             },
             _ => {}
         }
         Ok(())
     }
 
     /// Converts a Marionette JSON response into a `WebElement`.
     ///
     /// Note that it currently coerces all chrome elements, web frames, and web
     /// windows also into web elements.  This will change at a later point.
-    fn to_web_element(&self, json_data: &Json) -> WebDriverResult<WebElement> {
+    fn to_web_element(&self, json_data: &Value) -> WebDriverResult<WebElement> {
         let data = try_opt!(
             json_data.as_object(),
             ErrorStatus::UnknownError,
             "Failed to convert data to an object"
         );
 
         let chrome_element = data.get(CHROME_ELEMENT_KEY);
         let element = data.get(ELEMENT_KEY);
@@ -674,31 +571,30 @@ impl MarionetteSession {
                 .or(legacy_element)
                 .or(chrome_element)
                 .or(frame)
                 .or(window),
             ErrorStatus::UnknownError,
             "Failed to extract web element from Marionette response"
         );
         let id = try_opt!(
-            value.as_string(),
+            value.as_str(),
             ErrorStatus::UnknownError,
             "Failed to convert web element reference value to string"
         ).to_string();
         Ok(WebElement::new(id))
     }
 
     pub fn next_command_id(&mut self) -> u64 {
         self.command_id = self.command_id + 1;
         self.command_id
     }
 
     pub fn response(&mut self, msg: &WebDriverMessage<GeckoExtensionRoute>,
                     resp: MarionetteResponse) -> WebDriverResult<WebDriverResponse> {
-
         if resp.id != self.command_id {
             return Err(WebDriverError::new(ErrorStatus::UnknownError,
                                            format!("Marionette responses arrived out of sequence, expected {}, got {}",
                                                    self.command_id, resp.id)));
         }
 
         if let Some(error) = resp.error {
             return Err(error.into());
@@ -717,392 +613,336 @@ impl MarionetteSession {
                 WebDriverResponse::Void
             },
             // Things that simply return the contents of the marionette "value" property
             GetCurrentUrl | GetTitle | GetPageSource | GetWindowHandle | IsDisplayed(_) |
             IsSelected(_) | GetElementAttribute(_, _) | GetElementProperty(_, _) |
             GetCSSValue(_, _) | GetElementText(_) |
             GetElementTagName(_) | IsEnabled(_) | ExecuteScript(_) | ExecuteAsyncScript(_) |
             GetAlertText | TakeScreenshot | TakeElementScreenshot(_) => {
-                let value = try_opt!(resp.result.find("value"),
-                                     ErrorStatus::UnknownError,
-                                     "Failed to find value field");
-                //TODO: Convert webelement keys
-                WebDriverResponse::Generic(ValueResponse::new(value.clone()))
+                WebDriverResponse::Generic(resp.to_value_response(true)?)
             },
             GetTimeouts => {
                 let script = try_opt!(try_opt!(resp.result
-                                                   .find("script"),
+                                                   .get("script"),
                                                ErrorStatus::UnknownError,
                                                "Missing field: script")
                                           .as_u64(),
                                       ErrorStatus::UnknownError,
                                       "Failed to interpret script timeout duration as u64");
                 // Check for the spec-compliant "pageLoad", but also for "page load",
                 // which was sent by Firefox 52 and earlier.
-                let page_load = try_opt!(try_opt!(resp.result.find("pageLoad")
-                                                      .or(resp.result.find("page load")),
+                let page_load = try_opt!(try_opt!(resp.result.get("pageLoad")
+                                                      .or(resp.result.get("page load")),
                                                   ErrorStatus::UnknownError,
                                                   "Missing field: pageLoad")
                                              .as_u64(),
                                          ErrorStatus::UnknownError,
                                          "Failed to interpret page load duration as u64");
                 let implicit = try_opt!(try_opt!(resp.result
-                                                     .find("implicit"),
+                                                     .get("implicit"),
                                                  ErrorStatus::UnknownError,
                                                  "Missing field: implicit")
                                             .as_u64(),
                                         ErrorStatus::UnknownError,
                                         "Failed to interpret implicit search duration as u64");
 
                 WebDriverResponse::Timeouts(TimeoutsResponse {
                     script: script,
                     pageLoad: page_load,
                     implicit: implicit,
                 })
             },
             Status => panic!("Got status command that should already have been handled"),
             GetWindowHandles => {
-                WebDriverResponse::Generic(ValueResponse::new(resp.result.clone()))
+                WebDriverResponse::Generic(resp.to_value_response(false)?)
             },
             CloseWindow => {
                 let data = try_opt!(resp.result.as_array(),
                                     ErrorStatus::UnknownError,
                                     "Failed to interpret value as array");
                 let handles = try!(data.iter()
                                        .map(|x| {
-                                                Ok(try_opt!(x.as_string(),
+                                                Ok(try_opt!(x.as_str(),
                                                             ErrorStatus::UnknownError,
                                                             "Failed to interpret window handle as string")
                                                            .to_owned())
                                             })
                                        .collect());
                 WebDriverResponse::CloseWindow(CloseWindowResponse { window_handles: handles })
             },
             GetElementRect(_) => {
                 let x = try_opt!(
-                    try_opt!(resp.result.find("x"),
+                    try_opt!(resp.result.get("x"),
                              ErrorStatus::UnknownError,
                              "Failed to find x field").as_f64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret x as float");
 
                 let y = try_opt!(
-                    try_opt!(resp.result.find("y"),
+                    try_opt!(resp.result.get("y"),
                              ErrorStatus::UnknownError,
                              "Failed to find y field").as_f64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret y as float");
 
                 let width = try_opt!(
-                    try_opt!(resp.result.find("width"),
+                    try_opt!(resp.result.get("width"),
                              ErrorStatus::UnknownError,
                              "Failed to find width field").as_f64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret width as float");
 
                 let height = try_opt!(
-                    try_opt!(resp.result.find("height"),
+                    try_opt!(resp.result.get("height"),
                              ErrorStatus::UnknownError,
                              "Failed to find height field").as_f64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret width as float");
 
                 let rect = ElementRectResponse { x, y, width, height };
                 WebDriverResponse::ElementRect(rect)
             },
             FullscreenWindow | MinimizeWindow | MaximizeWindow | GetWindowRect |
             SetWindowRect(_) => {
                 let width = try_opt!(
-                    try_opt!(resp.result.find("width"),
+                    try_opt!(resp.result.get("width"),
                              ErrorStatus::UnknownError,
                              "Failed to find width field").as_u64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret width as positive integer");
 
                 let height = try_opt!(
-                    try_opt!(resp.result.find("height"),
+                    try_opt!(resp.result.get("height"),
                              ErrorStatus::UnknownError,
                              "Failed to find heigenht field").as_u64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret height as positive integer");
 
                 let x = try_opt!(
-                    try_opt!(resp.result.find("x"),
+                    try_opt!(resp.result.get("x"),
                              ErrorStatus::UnknownError,
                              "Failed to find x field").as_i64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret x as integer");
 
                 let y = try_opt!(
-                    try_opt!(resp.result.find("y"),
+                    try_opt!(resp.result.get("y"),
                              ErrorStatus::UnknownError,
                              "Failed to find y field").as_i64(),
                     ErrorStatus::UnknownError,
                     "Failed to interpret y as integer");
 
                 let rect = WindowRectResponse {
                     x: x as i32,
                     y: y as i32,
                     width: width as i32,
                     height: height as i32,
                 };
                 WebDriverResponse::WindowRect(rect)
             },
             GetCookies => {
-                let cookies = try!(self.process_cookies(&resp.result));
-                WebDriverResponse::Cookies(CookiesResponse { value: cookies })
+                // let cookies = try!(self.process_cookies(&resp.result));
+                //WebDriverResponse::Cookies(CookiesResponse { cookies: cookies })
+                let cookies: Vec<Cookie> = serde_json::from_value(resp.result)?;
+                WebDriverResponse::Cookies(CookiesResponse { cookies: cookies })
             },
             GetNamedCookie(ref name) => {
-                let mut cookies = try!(self.process_cookies(&resp.result));
+                // let mut cookies = try!(self.process_cookies(&resp.result));
+                let mut cookies: Vec<Cookie> = serde_json::from_value(resp.result)?;
                 cookies.retain(|x| x.name == *name);
                 let cookie = try_opt!(cookies.pop(),
                                       ErrorStatus::NoSuchCookie,
                                       format!("No cookie with name {}", name));
-                WebDriverResponse::Cookie(CookieResponse { value: cookie })
+                WebDriverResponse::Cookie(CookieResponse { cookie: cookie})
             }
             FindElement(_) | FindElementElement(_, _) => {
                 let element = try!(self.to_web_element(
-                    try_opt!(resp.result.find("value"),
+                    try_opt!(resp.result.get("value"),
                              ErrorStatus::UnknownError,
                              "Failed to find value field")));
-                WebDriverResponse::Generic(ValueResponse::new(element.to_json()))
+                WebDriverResponse::Generic(ValueResponse::new(serde_json::to_value(element)?))
             },
             FindElements(_) | FindElementElements(_, _) => {
                 let element_vec = try_opt!(resp.result.as_array(),
                                            ErrorStatus::UnknownError,
                                            "Failed to interpret value as array");
                 let elements = try!(element_vec.iter().map(
                     |x| {
                         self.to_web_element(x)
                     }).collect::<Result<Vec<_>, _>>());
+                // TODO: Remove unwrap
                 WebDriverResponse::Generic(ValueResponse::new(
-                    Json::Array(elements.iter().map(|x| {x.to_json()}).collect())))
+                    Value::Array(elements.iter().map(|x| serde_json::to_value(x).unwrap()).collect())))
             },
             GetActiveElement => {
                 let element = try!(self.to_web_element(
-                    try_opt!(resp.result.find("value"),
+                    try_opt!(resp.result.get("value"),
                              ErrorStatus::UnknownError,
                              "Failed to find value field")));
-                WebDriverResponse::Generic(ValueResponse::new(element.to_json()))
+                WebDriverResponse::Generic(ValueResponse::new(serde_json::to_value(element)?))
             },
             NewSession(_) => {
                 let session_id = try_opt!(
-                    try_opt!(resp.result.find("sessionId"),
+                    try_opt!(resp.result.get("sessionId"),
                              ErrorStatus::InvalidSessionId,
-                             "Failed to find sessionId field").as_string(),
+                             "Failed to find sessionId field").as_str(),
                     ErrorStatus::InvalidSessionId,
                     "sessionId is not a string");
 
                 let mut capabilities = try_opt!(
-                    try_opt!(resp.result.find("capabilities"),
+                    try_opt!(resp.result.get("capabilities"),
                              ErrorStatus::UnknownError,
                              "Failed to find capabilities field").as_object(),
                     ErrorStatus::UnknownError,
                     "capabilities field is not an object").clone();
 
                 capabilities.insert("moz:geckodriverVersion".into(), BuildInfo.into());
 
                 WebDriverResponse::NewSession(NewSessionResponse::new(
-                    session_id.to_string(), Json::Object(capabilities)))
+                    session_id.to_string(), Value::Object(capabilities.clone())))
             },
             DeleteSession => {
                 WebDriverResponse::DeleteSession
             },
             Extension(ref extension) => {
                 match extension {
                     &GeckoExtensionCommand::GetContext => {
-                        let value = try_opt!(resp.result.find("value"),
-                                             ErrorStatus::UnknownError,
-                                             "Failed to find value field");
-                        WebDriverResponse::Generic(ValueResponse::new(value.clone()))
+                        WebDriverResponse::Generic(resp.to_value_response(true)?)
                     },
                     &GeckoExtensionCommand::SetContext(_) => WebDriverResponse::Void,
                     &GeckoExtensionCommand::XblAnonymousChildren(_) => {
                         let els_vec = try_opt!(resp.result.as_array(),
                             ErrorStatus::UnknownError, "Failed to interpret body as array");
                         let els = try!(els_vec.iter().map(|x| self.to_web_element(x))
                             .collect::<Result<Vec<_>, _>>());
-                        WebDriverResponse::Generic(ValueResponse::new(
-                            Json::Array(els.iter().map(|el| el.to_json()).collect())))
+                        WebDriverResponse::Generic(ValueResponse::new(serde_json::to_value(els)?))
                     },
                     &GeckoExtensionCommand::XblAnonymousByAttribute(_, _) => {
-                        let el = try!(self.to_web_element(try_opt!(resp.result.find("value"),
+                        let el = try!(self.to_web_element(try_opt!(resp.result.get("value"),
                             ErrorStatus::UnknownError, "Failed to find value field")));
-                        WebDriverResponse::Generic(ValueResponse::new(el.to_json()))
+                        WebDriverResponse::Generic(ValueResponse::new(serde_json::to_value(el)?))
                     },
                     &GeckoExtensionCommand::InstallAddon(_) => {
-                        let value = try_opt!(resp.result.find("value"),
-                                             ErrorStatus::UnknownError,
-                                             "Failed to find value field");
-                        WebDriverResponse::Generic(ValueResponse::new(value.clone()))
+                        WebDriverResponse::Generic(resp.to_value_response(true)?)
                     },
                     &GeckoExtensionCommand::UninstallAddon(_) => WebDriverResponse::Void
                 }
             }
         })
     }
+}
 
-    fn process_cookies(&self, json_data: &Json) -> WebDriverResult<Vec<Cookie>> {
-        let value = try_opt!(json_data.as_array(),
-                             ErrorStatus::UnknownError,
-                             "Failed to interpret value as array");
-        value.iter().map(|x| {
-            let name = try_opt!(
-                try_opt!(x.find("name"),
-                         ErrorStatus::UnknownError,
-                         "Cookie must have a name field").as_string(),
-                ErrorStatus::UnknownError,
-                "Cookie must have string name").to_string();
-            let value = try_opt!(
-                try_opt!(x.find("value"),
-                         ErrorStatus::UnknownError,
-                         "Cookie must have a value field").as_string(),
-                ErrorStatus::UnknownError,
-                "Cookie must have a string value").to_string();
-            let path = try!(
-                Nullable::from_json(x.find("path").unwrap_or(&Json::Null),
-                                    |x| {
-                                        Ok((try_opt!(x.as_string(),
-                                                     ErrorStatus::UnknownError,
-                                                     "Cookie path must be string")).to_string())
-                                    }));
-            let domain = try!(
-                Nullable::from_json(x.find("domain").unwrap_or(&Json::Null),
-                                    |x| {
-                                        Ok((try_opt!(x.as_string(),
-                                                     ErrorStatus::UnknownError,
-                                                     "Cookie domain must be string")).to_string())
-                                    }));
-            let expiry = try!(
-                Nullable::from_json(x.find("expiry").unwrap_or(&Json::Null),
-                                    |x| {
-                                        Ok(Date::new(try_opt!(
-                                            x.as_u64(),
-                                            ErrorStatus::UnknownError,
-                                            "Cookie expiry must be a positive integer")))
-                                    }));
-            let secure = try_opt!(
-                x.find("secure").map_or(Some(false), |x| x.as_boolean()),
-                ErrorStatus::UnknownError,
-                "Cookie secure flag must be boolean");
-            let http_only = try_opt!(
-                x.find("httpOnly").map_or(Some(false), |x| x.as_boolean()),
-                ErrorStatus::UnknownError,
-                "Cookie httpOnly flag must be boolean");
+
+#[derive(Debug, PartialEq)]
+pub struct MarionetteCommand {
+    pub id: u64,
+    pub name: String,
+    pub params: Map<String, Value>
+}
 
-            let new_cookie = Cookie {
-                name: name,
-                value: value,
-                path: path,
-                domain: domain,
-                expiry: expiry,
-                secure: secure,
-                httpOnly: http_only,
-            };
-            Ok(new_cookie)
-        }).collect::<Result<Vec<_>, _>>()
+impl Serialize for MarionetteCommand {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let data = (&0, &self.id, &self.name, &self.params);
+        data.serialize(serializer)
     }
 }
 
-pub struct MarionetteCommand {
-    pub id: u64,
-    pub name: String,
-    pub params: BTreeMap<String, Json>
-}
-
 impl MarionetteCommand {
-    fn new(id: u64, name: String, params: BTreeMap<String, Json>) -> MarionetteCommand {
+    fn new(id: u64, name: String, params: Map<String, Value>) -> MarionetteCommand {
         MarionetteCommand {
             id: id,
             name: name,
             params: params,
         }
     }
 
     fn from_webdriver_message(id: u64,
-                              capabilities: Option<BTreeMap<String, Json>>,
+                              capabilities: Option<Map<String, Value>>,
                               msg: &WebDriverMessage<GeckoExtensionRoute>)
                               -> WebDriverResult<MarionetteCommand> {
         let (opt_name, opt_parameters) = match msg.command {
             Status => panic!("Got status command that should already have been handled"),
             AcceptAlert => {
                 // Needs to be updated to "WebDriver:AcceptAlert" for Firefox 63
                 (Some("WebDriver:AcceptDialog"), None)
             }
             AddCookie(ref x) => (Some("WebDriver:AddCookie"), Some(x.to_marionette())),
             CloseWindow => (Some("WebDriver:CloseWindow"), None),
             DeleteCookie(ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("name".to_string(), x.to_json());
+                let mut data = Map::new();
+                data.insert("name".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:DeleteCookie"), Some(Ok(data)))
             }
             DeleteCookies => (Some("WebDriver:DeleteAllCookies"), None),
             DeleteSession => {
-                let mut body = BTreeMap::new();
-                body.insert("flags".to_owned(), vec!["eForceQuit".to_json()].to_json());
+                let mut body = Map::new();
+                body.insert("flags".to_owned(),
+                    serde_json::to_value(vec!["eForceQuit".to_string()])?);
                 (Some("Marionette:Quit"), Some(Ok(body)))
             }
             DismissAlert => (Some("WebDriver:DismissAlert"), None),
             ElementClear(ref x) => (Some("WebDriver:ElementClear"), Some(x.to_marionette())),
             ElementClick(ref x) => (Some("WebDriver:ElementClick"), Some(x.to_marionette())),
             ElementSendKeys(ref e, ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), e.id.to_json());
-                data.insert("text".to_string(), x.text.to_json());
+                let mut data = Map::new();
+                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("text".to_string(), Value::String(x.text.clone()));
                 data.insert(
                     "value".to_string(),
-                    x.text
+                    serde_json::to_value(x.text
                         .chars()
                         .map(|x| x.to_string())
-                        .collect::<Vec<String>>()
-                        .to_json(),
+                        .collect::<Vec<String>>())?
                 );
                 (Some("WebDriver:ElementSendKeys"), Some(Ok(data)))
             }
             ElementTap(ref x) => (Some("singleTap"), Some(x.to_marionette())),
             ExecuteAsyncScript(ref x) => (
                 Some("WebDriver:ExecuteAsyncScript"),
                 Some(x.to_marionette()),
             ),
             ExecuteScript(ref x) => (Some("WebDriver:ExecuteScript"), Some(x.to_marionette())),
             FindElement(ref x) => (Some("WebDriver:FindElement"), Some(x.to_marionette())),
             FindElementElement(ref e, ref x) => {
                 let mut data = try!(x.to_marionette());
-                data.insert("element".to_string(), e.id.to_json());
+                data.insert("element".to_string(), Value::String(e.id.clone()));
                 (Some("WebDriver:FindElement"), Some(Ok(data)))
             }
             FindElements(ref x) => (Some("WebDriver:FindElements"), Some(x.to_marionette())),
             FindElementElements(ref e, ref x) => {
                 let mut data = try!(x.to_marionette());
-                data.insert("element".to_string(), e.id.to_json());
+                data.insert("element".to_string(), Value::String(e.id.clone()));
                 (Some("WebDriver:FindElements"), Some(Ok(data)))
             }
             FullscreenWindow => (Some("WebDriver:FullscreenWindow"), None),
             Get(ref x) => (Some("WebDriver:Navigate"), Some(x.to_marionette())),
             GetAlertText => (Some("WebDriver:GetAlertText"), None),
             GetActiveElement => (Some("WebDriver:GetActiveElement"), None),
             GetCookies | GetNamedCookie(_) => (Some("WebDriver:GetCookies"), None),
             GetCurrentUrl => (Some("WebDriver:GetCurrentURL"), None),
             GetCSSValue(ref e, ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), e.id.to_json());
-                data.insert("propertyName".to_string(), x.to_json());
+                let mut data = Map::new();
+                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("propertyName".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementCSSValue"), Some(Ok(data)))
             }
             GetElementAttribute(ref e, ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), e.id.to_json());
-                data.insert("name".to_string(), x.to_json());
+                let mut data = Map::new();
+                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("name".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementAttribute"), Some(Ok(data)))
             }
             GetElementProperty(ref e, ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), e.id.to_json());
-                data.insert("name".to_string(), x.to_json());
+                let mut data = Map::new();
+                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("name".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementProperty"), Some(Ok(data)))
             }
             GetElementRect(ref x) => (Some("WebDriver:GetElementRect"), Some(x.to_marionette())),
             GetElementTagName(ref x) => {
                 (Some("WebDriver:GetElementTagName"), Some(x.to_marionette()))
             }
             GetElementText(ref x) => (Some("WebDriver:GetElementText"), Some(x.to_marionette())),
             GetPageSource => (Some("WebDriver:GetPageSource"), None),
@@ -1120,201 +960,152 @@ impl MarionetteCommand {
             IsEnabled(ref x) => (Some("WebDriver:IsElementEnabled"), Some(x.to_marionette())),
             IsSelected(ref x) => (Some("WebDriver:IsElementSelected"), Some(x.to_marionette())),
             MaximizeWindow => (Some("WebDriver:MaximizeWindow"), None),
             MinimizeWindow => (Some("WebDriver:MinimizeWindow"), None),
             NewSession(_) => {
                 let caps = capabilities
                     .expect("Tried to create new session without processing capabilities");
 
-                let mut data = BTreeMap::new();
+                let mut data = Map::new();
                 for (k, v) in caps.iter() {
-                    data.insert(k.to_string(), v.to_json());
+                    data.insert(k.to_string(), serde_json::to_value(v)?);
                 }
 
                 (Some("WebDriver:NewSession"), Some(Ok(data)))
             }
             PerformActions(ref x) => (Some("WebDriver:PerformActions"), Some(x.to_marionette())),
             Refresh => (Some("WebDriver:Refresh"), None),
             ReleaseActions => (Some("WebDriver:ReleaseActions"), None),
             SendAlertText(ref x) => {
-                let mut data = BTreeMap::new();
-                data.insert("text".to_string(), x.text.to_json());
+                let mut data = Map::new();
+                data.insert("text".to_string(), Value::String(x.text.clone()));
                 data.insert(
                     "value".to_string(),
-                    x.text
+                    serde_json::to_value(x.text
                         .chars()
                         .map(|x| x.to_string())
-                        .collect::<Vec<String>>()
-                        .to_json(),
+                        .collect::<Vec<String>>())?
                 );
                 (Some("WebDriver:SendAlertText"), Some(Ok(data)))
             }
             SetTimeouts(ref x) => (Some("WebDriver:SetTimeouts"), Some(x.to_marionette())),
             SetWindowRect(ref x) => (Some("WebDriver:SetWindowRect"), Some(x.to_marionette())),
             SwitchToFrame(ref x) => (Some("WebDriver:SwitchToFrame"), Some(x.to_marionette())),
             SwitchToParentFrame => (Some("WebDriver:SwitchToParentFrame"), None),
             SwitchToWindow(ref x) => (Some("WebDriver:SwitchToWindow"), Some(x.to_marionette())),
             TakeElementScreenshot(ref e) => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), e.id.to_json());
-                data.insert("highlights".to_string(), Json::Array(vec![]));
-                data.insert("full".to_string(), Json::Boolean(false));
+                let mut data = Map::new();
+                data.insert("element".to_string(), serde_json::to_value(e)?);
+                // data.insert("id".to_string(), e.id.to_json());
+                data.insert("highlights".to_string(), Value::Array(vec![]));
+                data.insert("full".to_string(), Value::Bool(false));
                 (Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
             }
             TakeScreenshot => {
-                let mut data = BTreeMap::new();
-                data.insert("id".to_string(), Json::Null);
-                data.insert("highlights".to_string(), Json::Array(vec![]));
-                data.insert("full".to_string(), Json::Boolean(false));
+                let mut data = Map::new();
+                data.insert("id".to_string(), Value::Null);
+                data.insert("highlights".to_string(), Value::Array(vec![]));
+                data.insert("full".to_string(), Value::Bool(false));
                 (Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
             }
             Extension(ref extension) => match extension {
                 &GeckoExtensionCommand::GetContext => (Some("Marionette:GetContext"), None),
                 &GeckoExtensionCommand::InstallAddon(ref x) => {
                     (Some("Addon:Install"), Some(x.to_marionette()))
                 }
                 &GeckoExtensionCommand::SetContext(ref x) => {
                     (Some("Marionette:SetContext"), Some(x.to_marionette()))
                 }
                 &GeckoExtensionCommand::UninstallAddon(ref x) => {
                     (Some("Addon:Uninstall"), Some(x.to_marionette()))
                 }
                 &GeckoExtensionCommand::XblAnonymousByAttribute(ref e, ref x) => {
                     let mut data = try!(x.to_marionette());
-                    data.insert("element".to_string(), e.id.to_json());
+                    data.insert("element".to_string(), Value::String(e.id.clone()));
                     (Some("WebDriver:FindElement"), Some(Ok(data)))
                 }
                 &GeckoExtensionCommand::XblAnonymousChildren(ref e) => {
-                    let mut data = BTreeMap::new();
-                    data.insert("using".to_owned(), "anon".to_json());
-                    data.insert("value".to_owned(), Json::Null);
-                    data.insert("element".to_string(), e.id.to_json());
+                    let mut data = Map::new();
+                    data.insert("using".to_owned(), serde_json::to_value("anon")?);
+                    data.insert("value".to_owned(), Value::Null);
+                    data.insert("element".to_string(), serde_json::to_value(e.id.clone())?);
                     (Some("WebDriver:FindElements"), Some(Ok(data)))
                 }
             },
         };
 
         let name = try_opt!(opt_name,
                             ErrorStatus::UnsupportedOperation,
                             "Operation not supported");
-        let parameters = try!(opt_parameters.unwrap_or(Ok(BTreeMap::new())));
+        let parameters = try!(opt_parameters.unwrap_or(Ok(Map::new())));
 
         Ok(MarionetteCommand::new(id, name.into(), parameters))
     }
 }
 
-impl ToJson for MarionetteCommand {
-    fn to_json(&self) -> Json {
-        Json::Array(vec![Json::U64(0), self.id.to_json(), self.name.to_json(),
-                         self.params.to_json()])
-    }
-}
-
+#[derive(Debug, PartialEq)]
 pub struct MarionetteResponse {
     pub id: u64,
     pub error: Option<MarionetteError>,
-    pub result: Json,
+    pub result: Value,
+}
+
+impl<'de> Deserialize<'de> for MarionetteResponse {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        struct ResponseWrapper {
+            msg_type: u64,
+            id: u64,
+            error: Option<MarionetteError>,
+            result: Value,
+        }
+
+        let wrapper: ResponseWrapper = Deserialize::deserialize(deserializer)?;
+
+        if wrapper.msg_type != 1 {
+            return Err(de::Error::custom("Expected '1' in first element of response"));
+        };
+
+        Ok(MarionetteResponse {
+            id: wrapper.id,
+            error: wrapper.error,
+            result: wrapper.result})
+    }
 }
 
 impl MarionetteResponse {
-    fn from_json(data: &Json) -> WebDriverResult<MarionetteResponse> {
-        let data_array = try_opt!(data.as_array(),
-                                  ErrorStatus::UnknownError,
-                                  "Expected a json array");
-
-        if data_array.len() != 4 {
-            return Err(WebDriverError::new(
-                ErrorStatus::UnknownError,
-                "Expected an array of length 4"));
-        }
-
-        if data_array[0].as_u64() != Some(1) {
-            return Err(WebDriverError::new(ErrorStatus::UnknownError,
-                                           "Expected 1 in first element of response"));
-        };
-        let id = try_opt!(data[1].as_u64(),
-                          ErrorStatus::UnknownError,
-                          "Expected an integer id");
-        let error = if data[2].is_object() {
-            Some(try!(MarionetteError::from_json(&data[2])))
-        } else if data[2].is_null() {
-            None
-        } else {
-            return Err(WebDriverError::new(ErrorStatus::UnknownError,
-                                           "Expected object or null error"));
+    fn to_value_response(self, value_required: bool) -> WebDriverResult<ValueResponse> {
+        let value: &Value = match value_required {
+            true => {
+                try_opt!(self.result.get("value"),
+                         ErrorStatus::UnknownError, "Failed to find value field")
+            },
+            false => {
+                &self.result
+            }
         };
 
-        let result = if data[3].is_null() || data[3].is_object() || data[3].is_array() {
-            data[3].clone()
-        } else {
-            return Err(WebDriverError::new(ErrorStatus::UnknownError,
-                                           "Expected object params"));
-        };
-
-        Ok(MarionetteResponse {id: id,
-                               error: error,
-                               result: result})
+        Ok(ValueResponse {
+            value: value.clone(),
+        })
     }
 }
 
-impl ToJson for MarionetteResponse {
-    fn to_json(&self) -> Json {
-        Json::Array(vec![Json::U64(1), self.id.to_json(), self.error.to_json(),
-                         self.result.clone()])
-    }
-}
-
-#[derive(RustcEncodable, RustcDecodable)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct MarionetteError {
+    #[serde(rename = "error")]
     pub code: String,
     pub message: String,
     pub stacktrace: Option<String>
 }
 
-impl MarionetteError {
-    fn from_json(data: &Json) -> WebDriverResult<MarionetteError> {
-        if !data.is_object() {
-            return Err(WebDriverError::new(ErrorStatus::UnknownError,
-                                           "Expected an error object"));
-        }
-
-        let code = try_opt!(
-            try_opt!(data.find("error"),
-                     ErrorStatus::UnknownError,
-                     "Error value has no error code").as_string(),
-            ErrorStatus::UnknownError,
-            "Error status was not a string").into();
-        let message = try_opt!(
-            try_opt!(data.find("message"),
-                     ErrorStatus::UnknownError,
-                     "Error value has no message").as_string(),
-            ErrorStatus::UnknownError,
-            "Error message was not a string").into();
-        let stacktrace = match data.find("stacktrace") {
-            None | Some(&Json::Null) => None,
-            Some(x) => Some(try_opt!(x.as_string(),
-                                     ErrorStatus::UnknownError,
-                                     "Error message was not a string").into()),
-        };
-
-        Ok(MarionetteError { code, message, stacktrace })
-    }
-}
-
-impl ToJson for MarionetteError {
-    fn to_json(&self) -> Json {
-        let mut data = BTreeMap::new();
-        data.insert("error".into(), self.code.to_json());
-        data.insert("message".into(), self.message.to_json());
-        data.insert("stacktrace".into(), self.stacktrace.to_json());
-        Json::Object(data)
-    }
-}
-
 impl Into<WebDriverError> for MarionetteError {
     fn into(self) -> WebDriverError {
         let status = ErrorStatus::from(self.code);
         let message = self.message;
 
         if let Some(stack) = self.stacktrace {
             WebDriverError::new_with_stack(status, message, stack)
         } else {
@@ -1389,69 +1180,61 @@ impl MarionetteConnection {
                             ErrorStatus::UnknownError,
                             e.description().to_owned(),
                         ));
                     }
                 }
             }
         }
 
+        let data = self.handshake()?;
+        self.session.protocol = Some(data.protocol);
+        self.session.application_type = Some(data.application_type);
+
         debug!("Connected to Marionette on {}:{}", DEFAULT_HOST, self.port);
-        self.handshake()
+        Ok(())
     }
 
-    fn handshake(&mut self) -> WebDriverResult<()> {
-        let resp = try!(self.read_resp());
-        let handshake_data = try!(Json::from_str(&*resp));
-
-        let data = try_opt!(handshake_data.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Expected a json object in handshake");
+    fn handshake(&mut self) -> WebDriverResult<MarionetteHandshake> {
+        let resp = self.read_resp()?;
+        let data = serde_json::from_str::<MarionetteHandshake>(&resp)?;
 
-        self.session.protocol = Some(try_opt!(data.get("marionetteProtocol"),
-                                              ErrorStatus::UnknownError,
-                                              "Missing 'marionetteProtocol' field in handshake").to_string());
-
-        self.session.application_type = Some(try_opt!(data.get("applicationType"),
-                                              ErrorStatus::UnknownError,
-                                              "Missing 'applicationType' field in handshake").to_string());
-
-        if self.session.protocol != Some("3".into()) {
+        if data.protocol != 3 {
             return Err(WebDriverError::new(
                 ErrorStatus::UnknownError,
                 format!("Unsupported Marionette protocol version {}, required 3",
-                        self.session.protocol.as_ref().unwrap_or(&"<unknown>".into()))));
+                        data.protocol)));
         }
 
-        Ok(())
+        Ok(data)
     }
 
     pub fn close(&self) {
     }
 
-    fn encode_msg(&self, msg:Json) -> String {
-        let data = json::encode(&msg).unwrap();
-        format!("{}:{}", data.len(), data)
+    fn encode_msg(&self, msg: MarionetteCommand) -> WebDriverResult<String> {
+        let data = serde_json::to_string(&msg)?;
+
+        Ok(format!("{}:{}", data.len(), data))
     }
 
     pub fn send_command(&mut self,
-                        capabilities: Option<BTreeMap<String, Json>>,
+                        capabilities: Option<Map<String, Value>>,
                         msg: &WebDriverMessage<GeckoExtensionRoute>)
                         -> WebDriverResult<WebDriverResponse> {
         let id = self.session.next_command_id();
-        let command = try!(MarionetteCommand::from_webdriver_message(id, capabilities, msg));
+        let command = MarionetteCommand::from_webdriver_message(id, capabilities, msg)?;
+        let resp_data = self.send(command)?;
+        let data: MarionetteResponse = serde_json::from_str(&resp_data)?;
 
-        let resp_data = try!(self.send(command.to_json()));
-        let json_data: Json = try!(Json::from_str(&*resp_data));
-
-        self.session.response(msg, try!(MarionetteResponse::from_json(&json_data)))
+        self.session.response(msg, data)
     }
 
-    fn send(&mut self, msg: Json) -> WebDriverResult<String> {
-        let data = self.encode_msg(msg);
+    fn send(&mut self, msg: MarionetteCommand) -> WebDriverResult<String> {
+        let data = self.encode_msg(msg)?;
 
         match self.stream {
             Some(ref mut stream) => {
                 if stream.write(&*data.as_bytes()).is_err() {
                     let mut err = WebDriverError::new(ErrorStatus::UnknownError,
                                                       "Failed to write response to stream");
                     err.delete_session = true;
                     return Err(err);
@@ -1459,16 +1242,17 @@ impl MarionetteConnection {
             }
             None => {
                 let mut err = WebDriverError::new(ErrorStatus::UnknownError,
                                                   "Tried to write before opening stream");
                 err.delete_session = true;
                 return Err(err);
             }
         }
+
         match self.read_resp() {
             Ok(resp) => Ok(resp),
             Err(_) => {
                 let mut err = WebDriverError::new(ErrorStatus::UnknownError,
                                                   "Failed to decode response from marionette");
                 err.delete_session = true;
                 Err(err)
             }
@@ -1519,198 +1303,326 @@ impl MarionetteConnection {
         }
 
         // TODO(jgraham): Need to handle the error here
         Ok(String::from_utf8(payload).unwrap())
     }
 }
 
 trait ToMarionette {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>>;
-}
-
-impl ToMarionette for GetParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
-    }
-}
-
-impl ToMarionette for TimeoutsParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
-    }
-}
-
-impl ToMarionette for WindowRectParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
-    }
-}
-
-impl ToMarionette for SwitchToWindowParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("name".to_string(), self.handle.to_json());
-        Ok(data)
-    }
-}
-
-impl ToMarionette for LocatorParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(),
-                    ErrorStatus::UnknownError,
-                    "Expected an object")
-            .clone())
-    }
-}
-
-impl ToMarionette for SwitchToFrameParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        let key = match self.id {
-            FrameId::Null => None,
-            FrameId::Short(_) => Some("id"),
-            FrameId::Element(_) => Some("element"),
-        };
-        if let Some(x) = key {
-            data.insert(x.to_string(), self.id.to_json());
-        }
-        Ok(data)
-    }
-}
-
-impl ToMarionette for JavascriptCommandParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = self.to_json().as_object().unwrap().clone();
-        data.insert("newSandbox".to_string(), false.to_json());
-        data.insert("specialPowers".to_string(), false.to_json());
-        data.insert("scriptTimeout".to_string(), Json::Null);
-        Ok(data)
-    }
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>>;
 }
 
 impl ToMarionette for ActionsParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(),
-                    ErrorStatus::UnknownError,
-                    "Expected an object")
-            .clone())
-    }
-}
-
-impl ToMarionette for GetNamedCookieParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        Ok(try_opt!(self.to_json().as_object(),
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
                     ErrorStatus::UnknownError,
                     "Expected an object")
             .clone())
     }
 }
 
 impl ToMarionette for AddCookieParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut cookie = BTreeMap::new();
-        cookie.insert("name".to_string(), self.name.to_json());
-        cookie.insert("value".to_string(), self.value.to_json());
-        if self.path.is_value() {
-            cookie.insert("path".to_string(), self.path.to_json());
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut cookie = Map::new();
+        cookie.insert("name".to_string(), serde_json::to_value(&self.name)?);
+        cookie.insert("value".to_string(), serde_json::to_value(&self.value)?);
+        if self.path.is_some() {
+            cookie.insert("path".to_string(), serde_json::to_value(&self.path)?);
+        }
+        if self.domain.is_some() {
+            cookie.insert("domain".to_string(), serde_json::to_value(&self.domain)?);
+        }
+        if self.expiry.is_some() {
+            cookie.insert("expiry".to_string(), serde_json::to_value(&self.expiry)?);
         }
-        if self.domain.is_value() {
-            cookie.insert("domain".to_string(), self.domain.to_json());
+        cookie.insert("secure".to_string(), serde_json::to_value(self.secure)?);
+        cookie.insert("httpOnly".to_string(), serde_json::to_value(self.httpOnly)?);
+
+        let mut data = Map::new();
+        data.insert("cookie".to_string(), serde_json::to_value(cookie)?);
+        Ok(data)
+    }
+}
+
+impl ToMarionette for FrameId {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        match *self {
+            FrameId::Short(x) => data.insert("id".to_string(), serde_json::to_value(x)?),
+            FrameId::Element(ref x) => data.insert("element".to_string(),
+                                                   Value::Object(try!(x.to_marionette()))),
+        };
+        Ok(data)
+    }
+}
+
+impl ToMarionette for GetNamedCookieParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
+                    ErrorStatus::UnknownError,
+                    "Expected an object")
+            .clone())
+    }
+}
+
+impl ToMarionette for GetParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
+            ErrorStatus::UnknownError,
+            "Expected an object").clone())
+    }
+}
+
+impl ToMarionette for JavascriptCommandParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = serde_json::to_value(self)?.as_object().unwrap().clone();
+        data.insert("newSandbox".to_string(), Value::Bool(false));
+        data.insert("specialPowers".to_string(), Value::Bool(false));
+        data.insert("scriptTimeout".to_string(), Value::Null);
+        Ok(data)
+    }
+}
+
+impl ToMarionette for LocatorParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
+                    ErrorStatus::UnknownError,
+                    "Expected an object")
+            .clone())
+    }
+}
+
+impl ToMarionette for SwitchToFrameParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        let key = match self.id {
+            None => None,
+            Some(FrameId::Short(_)) => Some("id"),
+            Some(FrameId::Element(_)) => Some("element"),
+        };
+        if let Some(x) = key {
+            data.insert(x.to_string(), serde_json::to_value(&self.id)?);
         }
-        if self.expiry.is_value() {
-            cookie.insert("expiry".to_string(), self.expiry.to_json());
-        }
-        cookie.insert("secure".to_string(), self.secure.to_json());
-        cookie.insert("httpOnly".to_string(), self.httpOnly.to_json());
-        let mut data = BTreeMap::new();
-        data.insert("cookie".to_string(), Json::Object(cookie));
+        Ok(data)
+    }
+}
+
+impl ToMarionette for SwitchToWindowParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("name".to_string(), serde_json::to_value(self.handle.clone())?);
         Ok(data)
     }
 }
 
 impl ToMarionette for TakeScreenshotParameters {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
         let element = match self.element {
-            Nullable::Null => Json::Null,
-            Nullable::Value(ref x) => Json::Object(try!(x.to_marionette()))
+            None => Value::Null,
+            Some(ref x) => Value::Object(try!(x.to_marionette()))
         };
         data.insert("element".to_string(), element);
         Ok(data)
     }
 }
 
+impl ToMarionette for TimeoutsParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
+            ErrorStatus::UnknownError,
+            "Expected an object").clone())
+    }
+}
+
 impl ToMarionette for WebElement {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        data.insert("id".to_string(), self.id.to_json());
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        let mut data = Map::new();
+        data.insert("id".to_string(), serde_json::to_value(&self.id)?);
         Ok(data)
     }
 }
 
-impl<T: ToJson> ToMarionette for Nullable<T> {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        //Note this is a terrible hack. We don't want Nullable<T: ToJson+ToMarionette>
-        //so in cases where ToJson != ToMarionette you have to deal with the Nullable
-        //explicitly. This kind of suggests that the whole design is wrong.
-        Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
-    }
-}
-
-impl ToMarionette for FrameId {
-    fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
-        let mut data = BTreeMap::new();
-        match *self {
-            FrameId::Short(x) => data.insert("id".to_string(), x.to_json()),
-            FrameId::Element(ref x) => data.insert("element".to_string(),
-                                                   Json::Object(try!(x.to_marionette()))),
-            FrameId::Null => None
-        };
-        Ok(data)
+impl ToMarionette for WindowRectParameters {
+    fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
+        Ok(try_opt!(serde_json::to_value(self)?.as_object(),
+            ErrorStatus::UnknownError,
+            "Expected an object").clone())
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use marionette::{AddonInstallParameters, Parameters};
-    use rustc_serialize::json::Json;
+    use super::*;
     use std::io::Read;
     use std::fs::File;
-    use webdriver::error::WebDriverResult;
+    use webdriver::test::check_deserialize;
+
+    #[test]
+    fn test_json_addon_install_parameters_null() {
+        let json = r#""#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
 
     #[test]
-    fn test_addon_install_params_missing_path() {
-        let json_data: Json = Json::from_str(r#"{"temporary": true}"#).unwrap();
-        let res: WebDriverResult<AddonInstallParameters> = Parameters::from_json(&json_data);
-        assert!(res.is_err());
+    fn test_json_addon_install_parameters_empty() {
+        let json = r#"{}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_path() {
+        let json = r#"{"path": "/path/to.xpi", "temporary": true}"#;
+        let data = AddonInstallParameters {
+            path: "/path/to.xpi".to_string(),
+            temporary: true,
+        };
+
+        check_deserialize(&json, &data);
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_path_invalid_type() {
+        let json = r#"{"path": true, "temporary": true}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
     }
 
     #[test]
-    fn test_addon_install_params_with_both_path_and_base64() {
-        let json_data: Json = Json::from_str(
-            r#"{"path": "/path/to.xpi", "addon": "aGVsbG8=", "temporary": true}"#).unwrap();
-        let res: WebDriverResult<AddonInstallParameters> = Parameters::from_json(&json_data);
-        assert!(res.is_err());
+    fn test_json_addon_install_parameters_with_path_and_temporary_invalid_type() {
+        let json = r#"{"path": "/path/to.xpi", "temporary": "foo"}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_path_only() {
+        let json = r#"{"path": "/path/to.xpi"}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_addon() {
+        let json = r#"{"addon": "aGVsbG8=", "temporary": true}"#;
+        let data = serde_json::from_str::<AddonInstallParameters>(&json).unwrap();
+
+        assert_eq!(data.temporary, true);
+        let mut file = File::open(data.path).unwrap();
+        let mut contents = String::new();
+        file.read_to_string(&mut contents).unwrap();
+        assert_eq!(contents, "hello");
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_addon_invalid_type() {
+        let json = r#"{"addon": true, "temporary": true}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_addon_and_temporary_invalid_type() {
+        let json = r#"{"addon": "aGVsbG8=", "temporary": "foo"}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_addon_only() {
+        let json = r#"{"addon": "aGVsbG8="}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
     }
 
     #[test]
-    fn test_addon_install_params_with_path() {
-        let json_data: Json = Json::from_str(
-            r#"{"path": "/path/to.xpi", "temporary": true}"#).unwrap();
-        let parameters: AddonInstallParameters = Parameters::from_json(&json_data).unwrap();
-        assert_eq!(parameters.path, "/path/to.xpi");
-        assert_eq!(parameters.temporary, true);
+    fn test_json_install_parameters_with_temporary_only() {
+        let json = r#"{"temporary": true}"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_install_parameters_with_both_path_and_addon() {
+        let json = r#"{
+            "path":"/path/to.xpi",
+            "addon":"aGVsbG8=",
+            "temporary":true
+        }"#;
+
+        assert!(serde_json::from_str::<AddonInstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_uninstall_parameters_null() {
+        let json = r#""#;
+
+        assert!(serde_json::from_str::<AddonUninstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_uninstall_parameters_empty() {
+        let json = r#"{}"#;
+
+        assert!(serde_json::from_str::<AddonUninstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_addon_uninstall_parameters() {
+        let json = r#"{"id": "foo"}"#;
+        let data = AddonUninstallParameters {
+            id: "foo".to_string(),
+        };
+
+        check_deserialize(&json, &data);
     }
 
     #[test]
-    fn test_addon_install_params_with_base64() {
-        let json_data: Json = Json::from_str(
-            r#"{"addon": "aGVsbG8=", "temporary": true}"#).unwrap();
-        let parameters: AddonInstallParameters = Parameters::from_json(&json_data).unwrap();
+    fn test_json_addon_uninstall_parameters_id_invalid_type() {
+        let json = r#"{"id": true}"#;
+
+        assert!(serde_json::from_str::<AddonUninstallParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_gecko_context_parameters_content() {
+        let json = r#"{"context": "content"}"#;
+        let data = GeckoContextParameters {
+            context: GeckoContext::Content,
+        };
+
+        check_deserialize(&json, &data);
+    }
+
+    #[test]
+    fn test_json_gecko_context_parameters_chrome() {
+        let json = r#"{"context": "chrome"}"#;
+        let data = GeckoContextParameters {
+            context: GeckoContext::Chrome,
+        };
 
-        assert_eq!(parameters.temporary, true);
-        let mut file = File::open(parameters.path).unwrap();
-        let mut contents = String::new();
-        file.read_to_string(&mut contents).unwrap();
-        assert_eq!("hello", contents);
+        check_deserialize(&json, &data);
+    }
+
+    #[test]
+    fn test_json_gecko_context_parameters_context_missing() {
+        let json = r#"{}"#;
+
+        assert!(serde_json::from_str::<GeckoContextParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_gecko_context_parameters_context_null() {
+        let json = r#"{"context": null}"#;
+
+        assert!(serde_json::from_str::<GeckoContextParameters>(&json).is_err());
+    }
+
+    #[test]
+    fn test_json_gecko_context_parameters_context_invalid_value() {
+        let json = r#"{"context": "foo"}"#;
+
+        assert!(serde_json::from_str::<GeckoContextParameters>(&json).is_err());
     }
 }