On Tuesday, 12 November 2019 at 09:15:28 UTC, SealabJaster wrote:

And if you do allow things such as letting classes have a 'deserialise' member function which can be overloaded, you still need to create or be given an instance of the class beforehand, which brings things back around to the "constructor issue" in the latest post.

I don't quite know if there's actually an automatic solution for classes that covers most cases? Anything I can think of places limitations in some form or another.

They way I handled this in Orange [1] which allows to (de)serialize third party types which you don't have any control of. First, structs have the same problem as classes: there can be private members. They way this is handled is to use `.tupleof`. `.tupleof` will bypass the protection and allows to read and write private fields:

module foo;

class Foo
{
    private int a;
    int getA() { return a; }
}

module main;

import std.stdio;
import foo;

void main()
{
    auto foo = new Foo;
    foo.tupleof[0] = 3;
    assert(foo.getA == 3);
    assert(foo.tupleof[0] == 3);
}

`.tupleof` is used both for serialization and deserialization. To create an instance of a class use the same way as the runtime does. This function [2] is called by the compiler when the code contains "new Object" (extracted to Orange here [3]). This will not call a constructor, but that's fine since we will set the fields anyway directly after.

Orange also offers a way to tweak the (de)serialization process with hooks. You can define methods in your structs or classes with any of the following UDAs: `onSerializing`, `onSerialized`, `onDeserializing` and `onDeserialized`. These method will be called (if they exist) before/after the (de)serialization process [4]. This allows you do preform some post-processing that might be needed since no constructor has been called on deserialization.

When it comes to (de)serializing derived types through a base class reference Orange requires you do register all derived types [5].

Then there's the problem with non-mutable fields and instances as well. But you can just cast away those when deserializing.

There's also the issue with circular references [6]. But that's pretty easy to solve by storing all serialized instances to see if they have already been serialized or not.

[1] http://github.com/jacob-carlborg/orange

[2] https://github.com/dlang/druntime/blob/873fac33014c5af680c4bed69bb74cb0f192198a/src/rt/lifetime.d#L73

[3] https://github.com/jacob-carlborg/orange/blob/90f1dbb0097ba4a319805bfb7d109f7038418ac6/orange/util/Reflection.d#L149-L183

[4] https://github.com/jacob-carlborg/orange/blob/master/tests/Events.d

[5] https://github.com/jacob-carlborg/orange/blob/90f1dbb0097ba4a319805bfb7d109f7038418ac6/orange/serialization/Serializer.d#L241-L262

[6] https://github.com/jacob-carlborg/orange/blob/master/tests/CircularReference.d

--
/Jacob Carlborg

Reply via email to