Blog series to teach and show off D's metaprogramming by creating a JSON serialiser
Jacob Carlborg
doob at me.com
Tue Dec 3 09:47:44 UTC 2019
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
More information about the Digitalmars-d-announce
mailing list