End Goal for serializing FirefoxDriver (my question here) = making WebDriver faster!!
Below is a link that describes how to serialize an object. But it requires you implement from ISerializable for the object you are serializing. What I'd like to do is serialize an object that I did not define--an object based on a class in a 3rd party assembly (from a project reference) that is not implementing ISerializable. Is that possible? How can this be done?
http://www.switchonthecode.com/tutorials/csharp-tutorial-serialize-objects-to-a-file
Property (IWebDriver = interface type):
private IWebDriver driver;
Object Instance (FireFoxDriver is a class type):
driver = new FirefoxDriver(firefoxProfile);
================
3/21/2012 update after answer posted
Why would this throw an error? It doesn't like this line:
serializedObject.DriverInstance = (FirefoxDriver)driver;
...
Error:
Cannot implicitly convert type 'OpenQA.Selenium.IWebDriver' to 'OpenQA.Selenium.Firefox.FirefoxDriver'. An explicit conversion exists (are you missing a cast?)
Here is the code:
FirefoxDriverSerialized serializedObject = new FirefoxDriverSerialized();
Serializer serializer = new Serializer();
serializedObject = serializer.DeSerializeObject(@"C:\firefoxDriver.qa");
driver = serializedObject.DriverInstance;
if (driver == null)
{
driver = new FirefoxDriver(firefoxProfile);
serializedObject.DriverInstance = (FirefoxDriverSerialized)driver;
serializer.SerializeObject(@"C:\firefoxDriver.qa", serializedObject);
}
Here are the two Serializer classes I built:
public class Serializer
{
public Serializer()
{
}
public void SerializeObject(string filename, FirefoxDriverSerialized objectToSerialize)
{
Stream stream = File.Open(filename, FileMode.Create);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, objectToSerialize);
stream.Close();
}
public FirefoxDriverSerialized DeSerializeObject(string filename)
{
FirefoxDriverSerialized objectToSerialize;
Stream stream = File.Open(filename, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
objectToSerialize = (FirefoxDriverSerialized)bFormatter.Deserialize(stream);
stream.Close();
return objectToSerialize;
}
}
[Serializable()]
public class FirefoxDriverSerialized : FirefoxDriver, ISerializable
{
private FirefoxDriver driverInstance;
public FirefoxDriver DriverInstance
{
get { return this.driverInstance; }
set { this.driverInstance = value; }
}
public FirefoxDriverSerialized()
{
}
public FirefoxDriverSerialized(SerializationInfo info, StreamingContext ctxt)
{
this.driverInstance = (FirefoxDriver)info.GetValue("DriverInstance", typeof(FirefoxDriver));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("DriverInstance", this.driverInstance);
}
}
=================
3/23/2012 update #2 - fixed serialization/de-serialization, but having another issue
This fixed the calling code. Because we're deleting the *.qa file when we call the WebDriver.Quit() because that's when we chose to close the browser. This will kill off our cached driver as well. So if we start with a new browser window, we'll hit the catch block and create a new instance and save it to our *.qa file (in the serialized form).
FirefoxDriverSerialized serializedObject = new FirefoxDriverSerialized();
Serializer serializer = new Serializer();
try
{
serializedObject = serializer.DeSerializeObject(@"C:\firefoxDriver.qa");
driver = serializedObject.DriverInstance;
}
catch
{
driver = new FirefoxDriver(firefoxProfile);
serializedObject = new FirefoxDriverSerialized();
serializedObject.DriverInstance = (FirefoxDriver)driver;
serializer.SerializeObject(@"C:\firefoxDriver.qa", serializedObject);
}
However, still getting this exception:
Acu.QA.Main.Test_0055_GiftCertificate_UserCheckout:
SetUp : System.Runtime.Serialization.SerializationException : Type 'OpenQA.Selenium.Firefox.FirefoxDriver' in Assembly 'WebDriver, Version=2.16.0.0, Culture=neutral, PublicKeyToken=1c2bd1631853048f' is not marked as serializable.
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.
The 3rd line in this code block is throwing the exception:
public void SerializeObject(string filename, FirefoxDriverSerialized objectToSerialize)
{
Stream stream = File.Open(filename, FileMode.Create);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, objectToSerialize); // <=== this line
stream.Close();
}
====================
update #3 - 3/24/2012 - simplified FirefoxDriver instance by not passing anything to the constructor. I'll make it more complex later by passing in FirefoxProfile into the constructor. I still get the same exception as update #2.
Calling code:
FirefoxDriverSerialized serializedObject = new FirefoxDriverSerialized();
Serializer serializer = new Serializer();
try
{
serializedObject = serializer.DeSerializeObject(@"C:\firefoxDriver.qa");
driver = serializedObject.DriverInstance;
}
catch
{
//driver = new FirefoxDriver(firefoxProfile);
driver = new FirefoxDriver();
serializedObject.DriverInstance = (FirefoxDriver)driver;
serializer.SerializeObject(@"C:\firefoxDriver.qa", serializedObject);
}
Also had to add ": base()" to my constructor in my serialized object class:
[Serializable()]
public class FirefoxDriverSerialized : FirefoxDriver, ISerializable
{
private FirefoxDriver driverInstance;
public FirefoxDriver DriverInstance
{
get { return this.driverInstance; }
set { this.driverInstance = value; }
}
public FirefoxDriverSerialized() : base()
{
}
public FirefoxDriverSerialized(SerializationInfo info, StreamingContext ctxt)
{
this.driverInstance = (FirefoxDriver)info.GetValue("DriverInstance", typeof(FirefoxDriver));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("DriverInstance", this.driverInstance);
}
}
=============
Update #4 - just documenting stub signatures for OpenQA.Selenium.Firefox.FirefoxDriver class
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using System;
namespace OpenQA.Selenium.Firefox
{
public class FirefoxDriver : RemoteWebDriver, ITakesScreenshot
{
// CLASS DATA MEMBERS
//public static readonly bool AcceptUntrustedCertificates;
//public static readonly string BinaryCapabilityName;
//public static readonly bool DefaultEnableNativeEvents;
//public static readonly int DefaultPort;
//public static readonly string ProfileCapabilityName;
// CONSTRUCTORS
//public FirefoxDriver();
//public FirefoxDriver(FirefoxProfile profile);
//public FirefoxDriver(ICapabilities capabilities);
//public FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile);
//public FirefoxDriver(FirefoxBinary binary, FirefoxProfile profile, TimeSpan commandTimeout);
// PROPERTIES
protected FirefoxBinary Binary { get; }
protected FirefoxProfile Profile { get; }
// METHODS
//protected override RemoteWebElement CreateElement(string elementId);
//public Screenshot GetScreenshot();
//protected void PrepareEnvironment();
//protected override void StartClient();
//protected override void StopClient();
}
}
Create your own custom object that derives from
ISerializable
and the Object you want to serialize and serialize that custom object. This specific example won't work if the 3rd Party object isSealed
(there are other ways which can still be used withISerializable
).Updated Per Request
Updated Per Question Update
Update 2
Your new code is confusing me. I think you're looking for..
This is because
FirefoxDriverSerialized
is aFireFoxDriver
.Update 3
It is important to note that constructors are not called when an object is deserialized. This means that things that are normally constucted/set in the constructor won't be upon deserialization, which usually results in a
NullReferenceException
. The way around that is to implementISerializable
and explicity set the objects needed for the class to work (both for GetObjectData and the special deserializer constructor). This can be most difficult if the understanding of the object in question is not simple nor if we don't have the source for it.It is important to stress that you need to implement both GetObjectData as well as the special constructor when ISerializable is added to a class. The compiler will warn you if GetObjectData is missing, but since it is impossible to enforce the implementation of a constructor, no warnings will be given if the constructor is absent and an exception will be thrown when an attempt is made to deserialize a class without the constructor.
It isn't required to inherit from ISerializable. By default, the object must either inherit from ISerializable or be decorated with the SerializableAttribute. Without either of those, your best option is to use a SurrogateSelector. This will allow you to tell the serializer to serialize another object in its place based on how you define your surrogate.