@@ -21,23 +21,26 @@ Serializers must
21
21
* Inherit from ``net.corda.core.serialization.SerializationCustomSerializer ``
22
22
* Provide a proxy class to transform the object to and from
23
23
* Implement the ``toProxy `` and ``fromProxy `` methods
24
- * Be either included into CorDapp Jar or made known to the running process via ``amqp.custom.serialization.scanSpec ``
25
- system property. This system property may be necessary to be able to discover custom serializer in the classpath. At a minimum the value
26
- of the property should include comma separated set of packages where custom serializers located. Full syntax includes
27
- scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec> `
24
+ * Be either included into the CorDapp Jar or made known to the running process via the ``amqp.custom.serialization.scanSpec ``
25
+ system property. This system property may be necessary to be able to discover custom serializer in the classpath.
26
+ At a minimum the value of the property should include comma separated set of packages where custom serializers located.
27
+ Full syntax includes scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec> `
28
28
29
29
Serializers inheriting from ``SerializationCustomSerializer `` have to implement two methods and two types.
30
30
31
31
Example
32
32
-------
33
- Consider this example class:
33
+ Consider the following class:
34
34
35
35
.. sourcecode :: java
36
36
37
37
public final class Example {
38
38
private final Int a
39
39
private final Int b
40
40
41
+ // Because this is marked private the serialization framework will not
42
+ // consider it when looking to see which constructor should be used
43
+ // when serializing instances of this class.
41
44
private Example(Int a, Int b) {
42
45
this.a = a;
43
46
this.b = b;
@@ -52,23 +55,90 @@ Consider this example class:
52
55
Without a custom serializer we cannot serialize this class as there is no public constructor that facilitates the
53
56
initialisation of all of its properties.
54
57
55
- To be serializable by Corda this would require a custom serializer as follows:
58
+ .. note :: This is clearly a contrived example, simply making the constructor public would alleviate the issues.
59
+ However, for the purposes of this example we are assuming that for external reasons this cannot be done.
60
+
61
+ To be serializable by Corda this would require a custom serializer to be written that can transform the unserializable
62
+ class into a form we can serialize. Continuing the above example, this could be written as follows:
56
63
57
64
.. sourcecode :: kotlin
58
65
59
66
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
67
+ /**
68
+ * This is the actual proxy class that is used as an intermediate representation
69
+ * of the Example class
70
+ */
60
71
data class Proxy(val a: Int, val b: Int)
61
72
73
+ /**
74
+ * This method should be able to take an instance of the type being proxied and
75
+ * transpose it into that form, instantiating an instance of the Proxy object (it
76
+ * is this class instance that will be serialized into the byte stream.
77
+ */
62
78
override fun toProxy(obj: Example) = Proxy(obj.a, obj.b)
63
79
80
+ /**
81
+ * This method is used during deserialization. The bytes will have been read
82
+ * from the serialized blob and an instance of the Proxy class returned, we must
83
+ * now be able to transform that back into an instance of our original class.
84
+ *
85
+ * In our example this requires us to evoke the static *of * method on the
86
+ * Example class, transforming the serialized properties of the Proxy instance
87
+ * into a form expected by the construction method of Example.
88
+ */
64
89
override fun fromProxy(proxy: Proxy) : Example {
65
90
val constructorArg = IntArray(2);
66
91
constructorArg[0] = proxy.a
67
92
constructorArg[1] = proxy.b
68
- return Example.create (constructorArg)
93
+ return Example.of (constructorArg)
69
94
}
70
95
}
71
96
97
+ In the above ``ExampleSerializer `` is the actual serializer that will be loaded by the framework to
98
+ serialize instances of the ``Example `` type.
99
+
100
+ ``ExampleSerializer.Proxy `` is the intermediate representation used by the framework to represent
101
+ instances of ``Example `` within the wire format.
102
+
103
+ The Proxy Object
104
+ ----------------
105
+
106
+ The proxy object should be thought of as an intermediate representation that the serialization framework
107
+ can reason about. One is being written for a class because, for some reason, that class cannot be
108
+ introspected successfully but that framework. It is therefore important to note that the proxy class must
109
+ only contain elements that the framework can reason about.
110
+
111
+ The proxy class itself is distinct from the proxy serializer. The serializer must refer to the unserializable
112
+ type in the ``toProxy `` and ``fromProxy `` methods.
113
+
114
+ For example, the first thought a developer may have when implementing a proxy class is to simply *wrap * an
115
+ instance of the object being proxied. This is shown below
116
+
117
+ .. sourcecode :: kotlin
118
+
119
+ class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
120
+ /**
121
+ * In this example, we are trying to wrap the Example type to make it serializable
122
+ */
123
+ data class Proxy(val e: Example)
124
+
125
+ override fun toProxy(obj: Example) = Proxy(obj)
126
+
127
+ override fun fromProxy(proxy: Proxy) : Example {
128
+ return proxy.e
129
+ }
130
+ }
131
+
132
+ However, this will not work because what we've created is a recursive loop whereby synthesising a serializer
133
+ for the ``Example `` type requires synthesising one for ``ExampleSerializer.Proxy ``. However, that requires
134
+ one for ``Example `` and so on and so forth until we get a ``StackOverflowException ``.
135
+
136
+ The solution, as shown initially, is to create the intermediate form (the Proxy object) purely in terms
137
+ the serialization framework can reason about.
138
+
139
+ .. important :: When composing a proxy object for a class be aware that everything within that structure will be written
140
+ into the serialized byte stream.
141
+
72
142
Whitelisting
73
143
------------
74
144
By writing a custom serializer for a class it has the effect of adding that class to the whitelist, meaning such
0 commit comments