Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interfaces and object deserialization

Tags:

c#

oop

interface

I have an interface which defines a class to be serializable to a byte array.

public interface IByteSerializable
{
    byte[] GetBytes();
}

The natural partner to this is a deserialize method, which I want to return an object that implements IByteSerializable.

I'm stuggling with how to design such an interface.

This doesn't seem to make sense:

public interface IByteSerializable
{
    byte[] GetBytes();
    IByteSerializable GetObject(byte[] bytes);
}

Because the implementation of GetObject() can't be static and it doesn't make sense to use a dummy IByteSerializable object just to call the GetObject() method to deserialize the actual object I'm after.

It also doesn't seem to make sense to do this:

public interface IByteSerializableFactory
{
    IByteSerializable GetObject(byte[] bytes);
}

A Factory class could solve the problem, but this feels like it will result in class explosion. Also, the details of how a given IByteSerializable subclass are serialized then deserialized are co-dependent, so it makes sense to keep them in the same place, and not in two different classes. Obviously, the exact process required to deserialize a given IByteSerializable object depends entirely on how that object's GetBytes() method was written.

Is there a common design or pattern I can use to solve this issue?

like image 473
khargoosh Avatar asked Mar 18 '17 04:03

khargoosh


People also ask

Can you deserialize Interface C#?

Short answer: no. Deserialization involves creating an instance, and you can't create instances of interfaces.

What is JSON serialization and deserialization?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).

What is serialization and deserialization in MVC?

Serialize means convert an object instance to an XML document. Deserialize means convert an XML document into an object instance. Below are the steps to use Newtonsoft Jsonin mvc based application.

How does JSON deserialization work?

In Deserialization, it does the opposite of Serialization which means it converts JSON string to custom . Net object. In the following code, it creates a JavaScriptSerializer instance and calls Deserialize() by passing JSON data. It returns a custom object (BlogSites) from JSON data.

How do I deserialize an object to an interface?

You can't deserialize something directly to an interface, You need a concrete class to instantiate. This can be done by following some easy steps. The Newtonsoft.json Nuget package allows you to serialize and deserialize objects into JSON. Install-Package Newtonsoft.Json

What is serialization and deserialization in Java?

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.

Why do I get an invalidclassexception when deserializing an object?

If the reciever has loaded a class for the object that has different UID than that of corresponding sender’s class, the Deserialization will result in an InvalidClassException. A Serializable class can declare its own UID explicitly by declaring a field name. It must be static, final and of type long.

Does a child class need to implement a Serializable interface?

If a parent class has implemented Serializable interface then child class doesn’t need to implement it but vice-versa is not true. 2. Only non-static data members are saved via Serialization process. 3.


2 Answers

There are a lot of different opinions on interfaces, classes, and patterns when it comes to your question. My personal preference would be implementing an interface with a byte[] property and an abstract class with a virtual method (or even losing the interface altogether, which might not be an option for you and does not play well with DI and unit testing):

public interface IByteSerializable
{
    byte[] SerializableByteObject { get; }
}

public abstract class ByteSerializable : IByteSerializable
{
    public byte[] SerializableByteObject { get; }
    protected virtual byte[] GetBytes() {
        return SerializableByteObject;
    }
    public abstract IByteSerializable GetObject();
    //{    // You can make this method virtual and use Impl method:
           // GetObjectImpl(SerializableByteObject);
    //}
    protected internal IByteSerializable GetObjectImpl(byte[] bytes) {
        // If you need a default implementation (GetObject() should be protected virtual then)
        // return IByteSerializable...;
    }
}

I want to stress that interfaces VS abstract classes is an endless discussion. If you can do stuff without implementing interfaces and use just abstract classes - I would strongly recommend doing so.

Update 3/18/17: to reply to a comment (defining behavior is the purpose of an interface) and explain how I see it adding the explanation below.

In this scenario, the "behavior" we are defining is "An object should be convertible into a byte array. Conversion result should be convertible back into the same object." So we're actually defining behavior for an object and for a byte array (because after an object was deserialized - it's no longer the same object, it's just a byte array).

From my perspective, that's pure factory pattern scenario.

// Let's define an interface for our serializable type of objects factory
public interface IByteSerializableFactory<T>
{
     T CreateFromBytes(byte[] objectDataToUse);
     byte[] CovertToBytes(T objectToConvert);
}

// Interface for any class that needs a serialization factory
// This is not even necessary, but I like it to enforce people to implement simple methods that reference the factory.

public interface IByteSerializable<T>
{
    IByteSerializableFactory<T> GetFactory();
}

// Now a moment comes for us to have this kind of class. We need to build a factory first (because our interface requires a GetFactory() implementation. We can lose the IByteSerializable interface altogether, but then we lose a way to let people know which factory should be used.

public class SomeBaseClassSerializationFactory : IByteSerializableFactory<SomeBaseClass>
{
    public SomeBaseClass CreateFromBytes(byte[] objectDataToUse) { //...
        return new SomeClass();
    }
    public byte[] CovertToBytes(SomeBaseClass objectToConvert) { //...
        return new byte[1];
    }
}

// We have a factory, let's implement a class.

public abstract class SomeBaseClass : IByteSerializable<SomeBaseClass>
{
    public virtual IByteSerializableFactory<SomeBaseClass> GetFactory() { 
        return new SomeBaseClassSerializationFactory();                                             
    }
}

public class SomeClass : SomeBaseClass {
    // Now we're independent. Our derived classes do not need to implement anything.
    // If the way the derived class is serialized is different - we simply override the method
}

Update 2 3/18/17: to reply to a comment under a different answer (generic implementation with simple use of interface).

Unfortunately, there will be no clean way to do it. There is a dirty (my personal opinion: "BAD BAD BAD!") way by using some cheating, defining classes that define serialization methods and using reflection to return correct type. The example below will require a lot of custom logic in the serialization method for using correct fields with different types:

// You define an enum with action and a dictionary with a collection of serialization methods.
public enum SerializationAction {
    ToBytes,
    ToObject    
}

// It can also be an enum, but it's easier to test with a collection of strings.
public static readonly string[] SerializationKindList = new string[] {
    "FirstKind",
    "SecondKind"
};

// This generic class can have an implementation of all the handlers. Additional switching can be done by type, or reflection can be used to find properties for different classes and construct different classes.
public class SerializationMethod {
    public object ProcessByKind (string kindToUse, SerializationAction action, object objectToProcess) {
        if (kindToUse == "FirstKind") {
            if (action == SerializationAction.ToBytes) {
                return new byte[1]; 
            }

            return new SomeClass(); // These would need to be your hard implementations. Not clean.
        } else {
            throw new NotImplementedException();    
        }
    }
}

// This struct type defines the serialization method and is required for the interface implementation
public struct ByteSerialization
{
    public string SerializationTypeName { get; private set; }
    public ByteSerialization(string kindToUse) {
        if (!SerializationKindList.Contains(kindToUse)) {
            throw new ArgumentException();
        }

        SerializationTypeName = kindToUse;
    }
    public byte[] Deserialize(object objectToProcess) {
        var serializationMethod = new SerializationMethod();
        return (byte[])serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToBytes, objectToProcess);
    }
    public object Serialize(byte[] byteArrayToProcess) {
        var serializationMethod = new SerializationMethod();
        return serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToObject, byteArrayToProcess);
    }
}


// Interface for any class that needs to use generic serialization
public interface IByteSerializable
{
    ByteSerialization serializationType { get; }
}

// Creating extension methods for the interface to make the life easier
public static class IByteSerializableExtensions {
    public static byte[] DeserializeObjectIntoBytes(this IByteSerializable objectToProcess) {
        return objectToProcess.serializationType.Deserialize(objectToProcess);
    }
    public static void SerializeObjectFromBytes(this IByteSerializable objectToProcess, byte[] fromBytes) {
        var someObjectData = objectToProcess.serializationType.Serialize(fromBytes);
    }
}


// Abstract base class implementation with static readonly field.
// Only downside - there is no way to enforce the config of this field in the constructor from the interface.
// There also no way to make sure this field always gets set for other implementations of IByteSerializable
public abstract class SomeBaseClass : IByteSerializable
{
    private static readonly ByteSerialization _serializationType = new ByteSerialization("FirstKind");

    public ByteSerialization serializationType { get { return _serializationType; } }
}

public class SomeClass : SomeBaseClass {

}


// And here's how one would use it. You will need to create a new object of the class before serializing from bytes.
var someClass = new SomeClass();
var bytes = someClass.DeserializeObjectIntoBytes();
var someClass2 = new SomeClass();
var byteArray = new byte[1];
someClass2.SerializeObjectFromBytes(byteArray);
like image 126
Phil P. Avatar answered Oct 03 '22 11:10

Phil P.


Use a generic interface and each implementation can close the generic and return the closed type. It is upto the implementation to decide what type to return.

public interface ICustomSerializable<T> where T : class
{
    byte[] GetBytes();
    T Deserialize(byte[]);
}

public class Foo : ICustomSerializable<Foo>
{
    public byte[] GetBytes() {} 
    public Foo Deserialize(byte[]){} 
} 

public class Bar : ICustomSerializable<Bar>
{
    public byte[] GetBytes() {} 
    public Bar Deserialize(byte[]){} 
} 

If you have classes which do serialization in a common way then:

public abstract class Something
{
    public byte[] GetBytes() { //common code } 
} 

public class SomethingConcrete : Something, ICustomSerializable<SomethingConcrete>
{
    public SomethingConcrete Deserialize(byte[]){} 
} 
like image 30
CodingYoshi Avatar answered Oct 03 '22 11:10

CodingYoshi