File Coverage

blib/lib/WDDX.pm
Criterion Covered Total %
statement 9 140 6.4
branch 0 68 0.0
condition 0 15 0.0
subroutine 3 28 10.7
pod n/a
total 12 251 4.7


line stmt bran cond sub pod time code
1             package WDDX;
2              
3             =head1 NAME
4              
5             WDDX.pm - Module for reading and writing WDDX packets
6              
7             =head1 VERSION
8              
9             Version 1.02
10              
11             $Header: /home/cvs/wddx/WDDX.pm,v 1.4 2003/12/02 03:41:10 andy Exp $
12              
13             =cut
14              
15 3     3   1176 use vars qw( $VERSION );
  3         3  
  3         233  
16             $VERSION = "1.02";
17              
18             =head1 NAME
19              
20              
21             =head1 SYNOPSIS
22              
23             use WDDX;
24             my $wddx = new WDDX;
25            
26             # Serialization example
27            
28             my $wddx_hash = $wddx->hash( {
29             str => $wddx->string( "Welcome to WDDX!\n" ),
30             num => $wddx->number( -12.456 ),
31             date => $wddx->datetime( date ),
32             bool => $wddx->boolean( 1 ),
33             arr => $wddx->array( [
34             $wddx->boolean( 0 ),
35             $wddx->number( 10 ),
36             $wddx->string( "third element" ),
37             ] ),
38             rec => $wddx->recordset(
39             [ "NAME", "AGE" ],
40             [ "string", "number" ],
41             [
42             [ "John Doe", 34 ],
43             [ "Jane Doe", 25 ],
44             [ "Fred Doe", 90 ],
45             ]
46             ),
47             obj => $wddx->hash( {
48             str => $wddx->string( "a string" ),
49             num => $wddx->number( 3.14159 ),
50             } ),
51             bin => $wddx->binary( $img_data ),
52             null => $wddx->null(),
53             } );
54            
55             print $wddx->header;
56             print $wddx->serialize( $wddx_hash );
57            
58             # Deserialization example
59            
60             my $wddx_request = $wddx->deserialize( $packet );
61            
62             # Assume that our code expects an array
63             $wddx_request->type eq "array" or die "Invalid request";
64             my $array_ref = $wddx_request->as_arrayref;
65              
66              
67             =head1 DESCRIPTION
68              
69             =head2 About WDDX
70              
71             From L:
72              
73             =over 4
74              
75             The Web Distributed Data Exchange, or WDDX, is a free, open XML-based
76             technology that allows Web applications created with any platform to
77             easily exchange data with one another over the Web.
78              
79             =back
80              
81             =head2 WDDX and Perl
82              
83             WDDX defines basic data types that mirror the data types available in
84             other common programming languages. Many of these data types don't
85             have corresponding data types in Perl. To Perl, strings, numbers,
86             booleans, and dates are just scalars. However, in order to communicate
87             effectively with other languages (and this is the point of WDDX), you
88             do have to learn the basic WDDX data types. Here is a table that maps
89             the WDDX data type to Perl, along with the intermediate object WDDX.pm
90             represents it as:
91              
92             WDDX Type WDDX.pm Data Object Perl Type
93             --------- ------------------- ---------
94             String WDDX::String Scalar
95             Number WDDX::Number Scalar
96             Boolean WDDX::Boolean Scalar (1 or "")
97             Datetime WDDX::Datetime Scalar (seconds since epoch)
98             Null WDDX::Null Scalar (undef)
99             Binary WDDX::Binary Scalar
100             Array WDDX::Array Array
101             Struct WDDX::Struct Hash
102             Recordset WDDX::Recordset WDDX::Recordset
103              
104              
105             In languages that have data types similar to the WDDX data types, the
106             WDDX modules allow you to convert directly from a variable to a WDDX
107             packet and vice versa. This Perl implementation is different; here you
108             must always go through an intermediate stage where the data is
109             represented by an object with a corresponding data type. These objects
110             can be converted to a WDDX packet, converted to a basic Perl type, or
111             converted to JavaScript code (which will recreate the data for you in
112             JavaScript). We will refer to these objects as I
113             throughout this documentation.
114              
115             =head1 Requirements
116              
117             This module requires L and L, which are
118             both available on CPAN at L. Windows users note:
119             These modules use compiled code, but I have been told that they are both
120             included with recent distributions of ActiveState Perl.
121              
122             =cut
123              
124 3     3   14 use strict;
  3         4  
  3         79  
125 3     3   13 use Carp;
  3         3  
  3         6546  
126              
127             require WDDX::Parser;
128             require WDDX::Boolean;
129             require WDDX::Number;
130             require WDDX::Datetime;
131             require WDDX::String;
132             require WDDX::Array;
133             require WDDX::Recordset;
134             require WDDX::Struct;
135             require WDDX::Null;
136             require WDDX::Binary;
137              
138              
139             # Each of these must have a corresponding WDDX::* class;
140             # These are lowerclass while the WDDX::* name will have initial cap
141             @WDDX::Data_Types = qw( boolean number string datetime null
142             array struct recordset binary );
143              
144             $WDDX::XML_HEADER = "\n" .
145             "\n";
146             $WDDX::PACKET_HEADER = "
";
147             $WDDX::PACKET_FOOTER = "";
148              
149             # if this is defined, serialize() uses it to indent packet
150             $WDDX::INDENT = undef;
151              
152             # Create struct() as an alias to the hash() method:
153             *struct = \&hash;
154              
155             { my $i_hate_the_w_flag_sometimes = [
156             \@WDDX::Data_Types,
157             $WDDX::XML_HEADER,
158             $WDDX::PACKET_HEADER,
159             $WDDX::PACKET_FOOTER,
160             $WDDX::INDENT,
161             \&struct,
162             $WDDX::VERSION
163             ] }
164              
165             1;
166              
167              
168             =head1 METHODS
169              
170             =head2 new
171              
172             This creates a new WDDX object. You need one of these to do pretty much
173             anything else. It doesn't take any arguments.
174              
175             =cut
176              
177             sub new {
178 0     0     my $this = shift;
179 0   0       my $class = ref( $this ) || $this;
180            
181             # Currently no properties maintained in WDDX object
182 0           my $self = bless [], $class;
183 0           return $self;
184             }
185              
186              
187             =head2 C<< $wddx->deserialize( $string_or_filehandle ) >>
188              
189             This method deserializes a WDDX packet and returns a data object. Note
190             that you can pass either a string or a reference to an open filehandle
191             containing a packet (XML::Parser is flexible this way):
192              
193             $wddx_obj = $wddx->deserialize( $packet ); # OR
194             $wddx_obj = $wddx->deserialize( \*HANDLE );
195              
196             If WDDX.pm or the underlying L finds any errors with the
197             structure of the WDDX packet, then it will C with an error
198             message that identifies the problem. If you don't want this to terminate
199             your script, you will have to place this call within an C block
200             to trap the C.
201              
202             =cut
203              
204             sub deserialize {
205 0     0     my( $self, $xml ) = @_;
206 0           my $parser = new WDDX::Parser();
207            
208 0           return $parser->parse( $xml, $self );
209             }
210              
211              
212             =head2 C<< $wddx->serialize( $wddx_obj ) >>
213              
214             This accepts a data object as an argument and returns a WDDX packet.
215             This method calls the as_packet() method on the data object
216             it receives. However, this method does provide one feature that
217             C does not. If C<$WDDX::INDENT> is set to a defined value,
218             then the generated WDDX packet is indented using C<$WDDX::INDENT>
219             as the unit of indentation. Otherwise packets are generated without
220             extra whitespace.
221              
222             Note that the generated packet is not a valid XML document without the
223             header, see below.
224              
225             =cut
226              
227             sub serialize {
228 0     0     my( $self, $data ) = @_;
229            
230             croak "You may only serialize WDDX data objects" unless
231 0 0         eval { $data->can( "as_packet" ) };
  0            
232 0           my $packet = eval { $data->as_packet };
  0            
233 0 0         croak _shift_blame( $@ ) if $@;
234            
235 0 0         return defined( $WDDX::INDENT ) ? _xml_indent( $packet ) : $packet;
236             }
237              
238              
239             =head2 C<< $wddx->header >>
240              
241             This returns a header that should accompany every serialized packet you
242             send.
243              
244             =cut
245              
246             sub header {
247 0     0     return $WDDX::XML_HEADER;
248             }
249              
250              
251             sub string {
252 0     0     my( $this, $value ) = @_;
253 0           return new WDDX::String( $value );
254             }
255              
256             sub number {
257 0     0     my( $this, $value ) = @_;
258 0           return new WDDX::Number( $value );
259             }
260              
261             sub datetime {
262 0     0     my( $this, $value ) = @_;
263 0           return new WDDX::Datetime( $value );
264             }
265              
266             sub boolean {
267 0     0     my( $this, $value ) = @_;
268 0           return new WDDX::Boolean( $value );
269             }
270              
271             sub hash {
272 0     0     my( $this, $hashref ) = @_;
273            
274 0           my $var = eval {
275 0           new WDDX::Struct( $hashref );
276             };
277 0 0         croak _shift_blame( $@ ) if $@;
278            
279 0           return $var;
280             }
281              
282             sub array {
283 0     0     my( $this, $arrayref ) = @_;
284            
285 0           my $var = eval {
286 0           new WDDX::Array( $arrayref );
287             };
288 0 0         croak _shift_blame( $@ ) if $@;
289            
290 0           return $var;
291             }
292              
293             sub recordset {
294 0     0     my( $this, $names, $types, $tableref ) = @_;
295            
296 0           my $var = eval {
297 0           new WDDX::Recordset( $names, $types, $tableref );
298             };
299 0 0         croak _shift_blame( $@ ) if $@;
300            
301 0           return $var;
302             }
303              
304             sub binary {
305 0     0     my( $this, $value ) = @_;
306 0           return new WDDX::Binary( $value );
307             }
308              
309             sub null {
310 0     0     my( $this, $value ) = @_;
311 0           return new WDDX::Null( $value );
312             }
313              
314              
315             ############################################################
316             #
317             # Public Utility Methods (make life easier)
318             #
319              
320             sub scalar2wddx {
321 0     0     my( $wddx, $scalar, $type ) = @_;
322 0 0         $type = defined( $type ) ? lc $type : "string";
323            
324 0 0         croak "Will not encode a reference as a scalar" if ref $scalar;
325 0 0         my $var = eval "WDDX::\u$type->new( \$scalar )" or
326             croak "Unable to create object of type WDDX::\u$type: " .
327             _shift_blame( $@ );
328 0           return $var;
329             }
330              
331             sub hash2wddx {
332 0     0     my( $wddx, $hashref, $coderef ) = @_;
333 0           my $new_hash = {};
334 0     0     $coderef = sub { "" } unless
335 0 0 0       defined( $coderef ) && eval { &$coderef || 1 };
  0 0          
336            
337 0           while ( my( $name, $val ) = each %$hashref ) {
338            
339 0 0         eval { $val->can( "_serialize" ) } and do {
  0            
340 0           $new_hash->{$name} = $val;
341 0           next;
342             };
343            
344 0           my $type = lc $coderef->( $name => $val, "HASH" );
345 0 0         if ( $type ) {
346 0 0         ref( $val ) eq "HASH" and do {
347 0     0     $new_hash->{$name} = $wddx->hash2wddx ( $val, sub { $type } );
  0            
348 0           next;
349             };
350 0 0         ref( $val ) eq "ARRAY" and do {
351 0     0     $new_hash->{$name} = $wddx->array2wddx( $val, sub { $type } );
  0            
352 0           next;
353             };
354 0 0         my $var = eval "WDDX::\u$type->new( \$val )" or
355             croak "Unable to create object of type WDDX::\u$type: " .
356             _shift_blame( $@ );
357 0           $new_hash->{$name} = $var;
358 0           next;
359             }
360            
361 0 0         ref( $val ) eq "HASH" and do {
362 0           $new_hash->{$name} = hash2wddx ( $wddx, $val, $coderef );
363 0           next;
364             };
365 0 0         ref( $val ) eq "ARRAY" and do {
366 0           $new_hash->{$name} = array2wddx( $wddx, $val, $coderef );
367 0           next;
368             };
369            
370             # Scalars treated as strings by default
371 0           $new_hash->{$name} = $wddx->string( $val );
372             }
373 0           return $wddx->hash( $new_hash );
374             }
375              
376             sub array2wddx {
377 0     0     my( $wddx, $arrayref, $coderef ) = @_;
378 0           my $new_array = [];
379 0     0     $coderef = sub { "" } unless
380 0 0 0       defined( $coderef ) && eval { &$coderef || 1 };
  0 0          
381            
382 0           for ( my $i = 0; $i < @$arrayref; $i++ ) {
383 0           my $val = $arrayref->[$i];
384            
385 0 0         eval { $val->can( "_serialize" ) } and do {
  0            
386 0           push @$new_array, $val;
387 0           next;
388             };
389            
390 0           my $type = lc $coderef->( $i => $val, "ARRAY" );
391 0 0         if ( $type ) {
392 0 0         ref( $val ) eq "HASH" and do {
393 0     0     push @$new_array, hash2wddx( $wddx, $val, sub { $type } );
  0            
394 0           next;
395             };
396 0 0         ref( $val ) eq "ARRAY" and do {
397 0     0     push @$new_array, array2wddx( $wddx, $val, sub { $type } );
  0            
398 0           next;
399             };
400 0 0         my $var = eval "WDDX::\u$type->new( $i => \$val )" or
401             croak "Unable to create object of type WDDX::\u$type: " .
402             _shift_blame( $@ );
403 0           push @$new_array, $var;
404 0           next;
405             }
406            
407 0 0         ref( $val ) eq "HASH" and do {
408 0           push @$new_array, hash2wddx( $wddx, $val, $coderef );
409 0           next;
410             };
411            
412 0 0         ref( $val ) eq "ARRAY" and do {
413 0           push @$new_array, array2wddx( $wddx, $val, $coderef );
414 0           next;
415             };
416            
417             # Scalars treated as strings by default
418 0           push @$new_array, $wddx->string( $val );
419             }
420 0           return $wddx->array( $new_array );
421             }
422              
423             sub wddx2perl {
424 0     0     my( $self, $wddx_obj ) = @_;
425 0           my $result;
426 0 0         $result = $wddx_obj->as_scalar if $wddx_obj->can( "as_scalar" );
427 0 0         $result = $wddx_obj->as_hashref if $wddx_obj->type eq "hash";
428 0 0         $result = $wddx_obj->as_arrayref if $wddx_obj->type eq "array";
429 0 0         $result = $wddx_obj if $wddx_obj->type eq "recordset";
430 0           return $result;
431             }
432              
433              
434             ############################################################
435             #
436             # Private Subs
437             #
438              
439             # Takes a die message and strips any internal line refs
440             # This is necessary because we call public methods that invoke croak
441             # and croak would blame us even though we're just the messenger...
442             sub _shift_blame {
443 0     0     my $msg = shift;
444 0           $msg =~ s/ at \S*WDDX.*\.pm line \d+//g;
445 0           $msg =~ s/\n\nFile '.*'; Line \d+//g; # MacPerl thinks different
446 0           chomp $msg;
447 0           return $msg;
448             }
449              
450              
451             # This uses regex matches to do indentation based on whether tag
452             # starts with or >
453             # It's called by serialize() if $WDDX::INDENT is defined
454             sub _xml_indent {
455 0     0     my $xml = shift;
456 0           my $indent = $WDDX::INDENT;
457 0           my $level = 0;
458            
459             # It ain't pretty but it works...
460 0           $xml =~ s{ (>?)\s*(< ([?!/]?) [^>/]* (/?) ) }{
461             # print "Matched: $&\n 1: $1\n 2: $2\n 3: $3\n 4: $4\n";
462 0 0 0       $level-- if $3 eq "/" && not $4;
463 0 0         my $result = $1 ? "$1\n" . ( $indent x $level ) . $2 : $2;
464 0 0 0       $level++ unless $3 || $4;
465 0           $result;
466             }egx;
467 0           return $xml;
468             }
469              
470             __END__