.NET: Serializing object to a file from a 3rd part

2020-06-16 05:40发布

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();
    }
}

2条回答
趁早两清
2楼-- · 2020-06-16 06:26

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 is Sealed (there are other ways which can still be used with ISerializable).

Updated Per Request

Updated Per Question Update

public class MyFirefoxDriver : FirefoxDriver, ISerializable
{
  public MyFirefoxDriver(<Interface/Class> firefoxProfile)
    :base(firefoxProfile)
  {
  }

  void GetObjectData(SerializationInfo info, StreamingContext context)
  {
    // Properties needing to be serialized
    info.AddValue("SomeProperty", base.SomeProperty);
  }
}

Update 2

Your new code is confusing me. I think you're looking for..

serializedObject = serializer.DeSerializeObject(@"C:\firefoxDriver.qa");
driver = serializedObject;

This is because FirefoxDriverSerialized is a FireFoxDriver.

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 implement ISerializable 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.

public class MyObject
{
  public MyObject()
  {
    this.SomeOtherObject = new MyObject2();
  }
  public string Name { get; set; }
  public MyObject2 SomeOtherObject { get; set; }
}

public class MyObjectSerializable : MyObject, ISerializable
{
  protected MyObjectSerializable(SerializationInfo si, StreamingContext context) 
  {
    // base() is never called during deserialization
    // so use the special ISerializable constructor to set the value of the object
    // why not add it to the si.AddValue?
    // because, most likely in this question, it is not a [Serializable] object either
    // so we have to treat it differently as well
    this.SomeOtherObject = new MyObject2();
  }

  public override void GetObjectData(SerializationInfo si, StreamingContext context)
  {
    si.AddValue("Name", Name);
  }
}
查看更多
Emotional °昔
3楼-- · 2020-06-16 06:30

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.

查看更多
登录 后发表回答