… | |
… | |
17 | |
17 | |
18 | package Types::Serialiser; |
18 | package Types::Serialiser; |
19 | |
19 | |
20 | use common::sense; # required to suppress annoying warnings |
20 | use common::sense; # required to suppress annoying warnings |
21 | |
21 | |
22 | our $VERSION = 0.02; |
22 | our $VERSION = '1.0'; |
23 | |
23 | |
24 | =head1 SIMPLE SCALAR CONSTANTS |
24 | =head1 SIMPLE SCALAR CONSTANTS |
25 | |
25 | |
26 | Simple scalar constants are values that are overloaded to act like simple |
26 | Simple scalar constants are values that are overloaded to act like simple |
27 | Perl values, but have (class) type to differentiate them from normal Perl |
27 | Perl values, but have (class) type to differentiate them from normal Perl |
… | |
… | |
101 | |
101 | |
102 | BEGIN { |
102 | BEGIN { |
103 | # for historical reasons, and to avoid extra dependencies in JSON::PP, |
103 | # for historical reasons, and to avoid extra dependencies in JSON::PP, |
104 | # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. |
104 | # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. |
105 | package JSON::PP::Boolean; |
105 | package JSON::PP::Boolean; |
|
|
106 | |
106 | *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; |
107 | *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; |
|
|
108 | } |
|
|
109 | |
|
|
110 | { |
|
|
111 | # this must done before blessing to work around bugs |
|
|
112 | # in perl < 5.18 (it seems to be fixed in 5.18). |
|
|
113 | package Types::Serialiser::BooleanBase; |
|
|
114 | |
|
|
115 | use overload |
|
|
116 | "0+" => sub { ${$_[0]} }, |
|
|
117 | "++" => sub { $_[0] = ${$_[0]} + 1 }, |
|
|
118 | "--" => sub { $_[0] = ${$_[0]} - 1 }, |
|
|
119 | fallback => 1; |
|
|
120 | |
|
|
121 | @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; |
107 | } |
122 | } |
108 | |
123 | |
109 | our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; |
124 | our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; |
110 | our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; |
125 | our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; |
111 | our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; |
126 | our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; |
… | |
… | |
116 | |
131 | |
117 | sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
132 | sub is_bool ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
118 | sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
133 | sub is_true ($) { $_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
119 | sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
134 | sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } |
120 | sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } |
135 | sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } |
121 | |
|
|
122 | package Types::Serialiser::BooleanBase; |
|
|
123 | |
|
|
124 | use overload |
|
|
125 | "0+" => sub { ${$_[0]} }, |
|
|
126 | "++" => sub { $_[0] = ${$_[0]} + 1 }, |
|
|
127 | "--" => sub { $_[0] = ${$_[0]} - 1 }, |
|
|
128 | fallback => 1; |
|
|
129 | |
|
|
130 | @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; |
|
|
131 | |
136 | |
132 | package Types::Serialiser::Error; |
137 | package Types::Serialiser::Error; |
133 | |
138 | |
134 | sub error { |
139 | sub error { |
135 | require Carp; |
140 | require Carp; |
… | |
… | |
152 | While it is possible to use an isa test, directly comparing stash pointers |
157 | While it is possible to use an isa test, directly comparing stash pointers |
153 | is faster and guaranteed to work. |
158 | is faster and guaranteed to work. |
154 | |
159 | |
155 | For historical reasons, the C<Types::Serialiser::Boolean> stash is |
160 | For historical reasons, the C<Types::Serialiser::Boolean> stash is |
156 | just an alias for C<JSON::PP::Boolean>. When printed, the classname |
161 | just an alias for C<JSON::PP::Boolean>. When printed, the classname |
157 | withh usually be C<JSON::PP::Boolean>, but isa tests and stash pointer |
162 | with usually be C<JSON::PP::Boolean>, but isa tests and stash pointer |
158 | comparison will normally work correctly (i.e. Types::Serialiser::true ISA |
163 | comparison will normally work correctly (i.e. Types::Serialiser::true ISA |
159 | JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). |
164 | JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). |
160 | |
165 | |
161 | =head1 A GENERIC OBJECT SERIALIATION PROTOCOL |
166 | =head1 A GENERIC OBJECT SERIALIATION PROTOCOL |
162 | |
167 | |
… | |
… | |
172 | When the encoder encounters an object that it cannot otherwise encode (for |
177 | When the encoder encounters an object that it cannot otherwise encode (for |
173 | example, L<CBOR::XS> can encode a few special types itself, and will first |
178 | example, L<CBOR::XS> can encode a few special types itself, and will first |
174 | attempt to use the special C<TO_CBOR> serialisation protocol), it will |
179 | attempt to use the special C<TO_CBOR> serialisation protocol), it will |
175 | look up the C<FREEZE> method on the object. |
180 | look up the C<FREEZE> method on the object. |
176 | |
181 | |
|
|
182 | Note that the C<FREEZE> method will normally be called I<during> encoding, |
|
|
183 | and I<MUST NOT> change the data structure that is being encoded in any |
|
|
184 | way, or it might cause memory corruption or worse. |
|
|
185 | |
177 | If it exists, it will call it with two arguments: the object to |
186 | If it exists, it will call it with two arguments: the object to serialise, |
178 | serialise, and a constant string that indicates the name of the |
187 | and a constant string that indicates the name of the data model. For |
179 | serialisationformat. For example L<CBOR::XS> uses C<CBOR>, and L<JSON> and |
188 | example L<CBOR::XS> uses C<CBOR>, and the L<JSON> and L<JSON::XS> modules |
180 | L<JSON::XS> (or any other JSON serialiser), would use C<JSON> as second |
189 | (or any other JSON serialiser), would use C<JSON> as second argument. |
181 | argument. |
|
|
182 | |
190 | |
183 | The C<FREEZE> method can then return zero or more values to identify the |
191 | The C<FREEZE> method can then return zero or more values to identify the |
184 | object instance. The serialiser is then supposed to encode the class name |
192 | object instance. The serialiser is then supposed to encode the class name |
185 | and all of these return values (which must be encodable in the format) |
193 | and all of these return values (which must be encodable in the format) |
186 | using the relevant form for perl objects. In CBOR for example, there is a |
194 | using the relevant form for Perl objects. In CBOR for example, there is a |
187 | registered tag number for encoded perl objects. |
195 | registered tag number for encoded perl objects. |
|
|
196 | |
|
|
197 | The values that C<FREEZE> returns must be serialisable with the serialiser |
|
|
198 | that calls it. Therefore, it is recommended to use simple types such as |
|
|
199 | strings and numbers, and maybe array references and hashes (basically, the |
|
|
200 | JSON data model). You can always use a more complex format for a specific |
|
|
201 | data model by checking the second argument, the data model. |
|
|
202 | |
|
|
203 | The "data model" is not the same as the "data format" - the data model |
|
|
204 | indicates what types and kinds of return values can be returned from |
|
|
205 | C<FREEZE>. For example, in C<CBOR> it is permissible to return tagged CBOR |
|
|
206 | values, while JSON does not support these at all, so C<JSON> would be a |
|
|
207 | valid (but too limited) data model name for C<CBOR::XS>. similarly, a |
|
|
208 | serialising format that supports more or less the same data model as JSON |
|
|
209 | could use C<JSON> as data model without losing anything. |
188 | |
210 | |
189 | =head2 DECODING |
211 | =head2 DECODING |
190 | |
212 | |
191 | When the decoder then encounters such an encoded perl object, it should |
213 | When the decoder then encounters such an encoded perl object, it should |
192 | look up the C<THAW> method on the stored classname, and invoke it with the |
214 | look up the C<THAW> method on the stored classname, and invoke it with the |
193 | classname, the constant string to identify the format, and all the return |
215 | classname, the constant string to identify the data model/data format, and |
194 | values returned by C<FREEZE>. |
216 | all the return values returned by C<FREEZE>. |
195 | |
217 | |
196 | =head2 EXAMPLES |
218 | =head2 EXAMPLES |
197 | |
219 | |
198 | See the C<OBJECT SERIALISATION> section in the L<CBOR::XS> manpage for |
220 | See the C<OBJECT SERIALISATION> section in the L<CBOR::XS> manpage for |
199 | more details, an example implementation, and code examples. |
221 | more details, an example implementation, and code examples. |
200 | |
222 | |
201 | Here is an example C<FREEZE>/C<THAW> method pair: |
223 | Here is an example C<FREEZE>/C<THAW> method pair: |
202 | |
224 | |
203 | sub My::Object::FREEZE { |
225 | sub My::Object::FREEZE { |
204 | my ($self, $serialiser) = @_; |
226 | my ($self, $model) = @_; |
205 | |
227 | |
206 | ($self->{type}, $self->{id}, $self->{variant}) |
228 | ($self->{type}, $self->{id}, $self->{variant}) |
207 | } |
229 | } |
208 | |
230 | |
209 | sub My::Object::THAW { |
231 | sub My::Object::THAW { |
210 | my ($class, $serialiser, $type, $id, $variant) = @_; |
232 | my ($class, $model, $type, $id, $variant) = @_; |
211 | |
233 | |
212 | $class-<new (type => $type, id => $id, variant => $variant) |
234 | $class->new (type => $type, id => $id, variant => $variant) |
213 | } |
235 | } |
214 | |
236 | |
215 | =head1 BUGS |
237 | =head1 BUGS |
216 | |
238 | |
217 | The use of L<overload> makes this module much heavier than it should be |
239 | The use of L<overload> makes this module much heavier than it should be |