|
|
| |
WDDX(3) |
User Contributed Perl Documentation |
WDDX(3) |
WDDX.pm - Module for reading and writing WDDX packets
Version 1.02
$Header: /home/cvs/wddx/WDDX.pm,v 1.4 2003/12/02 03:41:10 andy Exp $
use WDDX;
my $wddx = new WDDX;
# Serialization example
my $wddx_hash = $wddx->hash( {
str => $wddx->string( "Welcome to WDDX!\n" ),
num => $wddx->number( -12.456 ),
date => $wddx->datetime( date ),
bool => $wddx->boolean( 1 ),
arr => $wddx->array( [
$wddx->boolean( 0 ),
$wddx->number( 10 ),
$wddx->string( "third element" ),
] ),
rec => $wddx->recordset(
[ "NAME", "AGE" ],
[ "string", "number" ],
[
[ "John Doe", 34 ],
[ "Jane Doe", 25 ],
[ "Fred Doe", 90 ],
]
),
obj => $wddx->hash( {
str => $wddx->string( "a string" ),
num => $wddx->number( 3.14159 ),
} ),
bin => $wddx->binary( $img_data ),
null => $wddx->null(),
} );
print $wddx->header;
print $wddx->serialize( $wddx_hash );
# Deserialization example
my $wddx_request = $wddx->deserialize( $packet );
# Assume that our code expects an array
$wddx_request->type eq "array" or die "Invalid request";
my $array_ref = $wddx_request->as_arrayref;
From <http://www.wddx.org/>:
The Web Distributed Data Exchange, or WDDX, is a free,
open XML-based technology that allows Web applications created with any
platform to easily exchange data with one another over the Web.
WDDX defines basic data types that mirror the data types available in other
common programming languages. Many of these data types don't have
corresponding data types in Perl. To Perl, strings, numbers, booleans, and
dates are just scalars. However, in order to communicate effectively with
other languages (and this is the point of WDDX), you do have to learn the
basic WDDX data types. Here is a table that maps the WDDX data type to Perl,
along with the intermediate object WDDX.pm represents it as:
WDDX Type WDDX.pm Data Object Perl Type
--------- ------------------- ---------
String WDDX::String Scalar
Number WDDX::Number Scalar
Boolean WDDX::Boolean Scalar (1 or "")
Datetime WDDX::Datetime Scalar (seconds since epoch)
Null WDDX::Null Scalar (undef)
Binary WDDX::Binary Scalar
Array WDDX::Array Array
Struct WDDX::Struct Hash
Recordset WDDX::Recordset WDDX::Recordset
In languages that have data types similar to the WDDX data types,
the WDDX modules allow you to convert directly from a variable to a WDDX
packet and vice versa. This Perl implementation is different; here you must
always go through an intermediate stage where the data is represented by an
object with a corresponding data type. These objects can be converted to a
WDDX packet, converted to a basic Perl type, or converted to JavaScript code
(which will recreate the data for you in JavaScript). We will refer to these
objects as data objects throughout this documentation.
This module requires XML::Parser and MIME::Base64, which are both available on
CPAN at <http://www.cpan.org/>. Windows users note: These modules use
compiled code, but I have been told that they are both included with recent
distributions of ActiveState Perl.
This creates a new WDDX object. You need one of these to do pretty much anything
else. It doesn't take any arguments.
This method deserializes a WDDX packet and returns a data object. Note that you
can pass either a string or a reference to an open filehandle containing a
packet (XML::Parser is flexible this way):
$wddx_obj = $wddx->deserialize( $packet ); # OR
$wddx_obj = $wddx->deserialize( \*HANDLE );
If WDDX.pm or the underlying XML::Parser finds any errors with the
structure of the WDDX packet, then it will
"die" with an error message that
identifies the problem. If you don't want this to terminate your script, you
will have to place this call within an
"eval" block to trap the
"die".
This accepts a data object as an argument and returns a WDDX packet. This method
calls the as_packet() method on the data object it receives. However,
this method does provide one feature that
"as_packet()" does not. If
$WDDX::INDENT is set to a defined value, then the
generated WDDX packet is indented using $WDDX::INDENT
as the unit of indentation. Otherwise packets are generated without extra
whitespace.
Note that the generated packet is not a valid XML document without
the header, see below.
This returns a header that should accompany every serialized packet you send.
All of the WDDX data objects share the following common methods:
- $wddx_obj->type
- This returns the data type of the object. It is lowercase and maps to the
package name without the WDDX prefix. For example, type will return
"string" for WDDX::String objects, "datetime" for
WDDX::Datetime objects, etc.
- $wddx_obj->as_packet
- This returns a WDDX packet for the object. You can also do this by passing
the object to the "$wddx-"serialize>
method. See the warning in
"$wddx-"header>.
- $wddx_obj->as_javascript( $js_varname )
- This method takes the name of a JavaScript variable and returns the actual
JavaScript code to assign this data object to the given JavaScript
variable. No temporary variables are created to avoid any danger of
variable name collisions.
Example:
$options[0] = $wddx->string( "First Choice" );
$options[1] = $wddx->string( "Second Choice" );
$options[2] = $wddx->string( "Third Choice" );
$w_array = $wddx->array( \@options );
print $w_array->as_javascript( "myArray" );
This prints the text (new lines added for readability):
myArray=new Array();
myArray[0]="First Choice";
myArray[1]="Second Choice";
myArray[2]="Third Choice";
All data types are supported, and arrays and hashes (structs)
can nest to any level. Recordset and binary objects require the
JavaScript WddxRecordset and WddxBinary constructors. The easiest way to
include these is to add a reference to the wddx.js file:
<SCRIPT NAME="javascript" SRC="wddx.js"></SCRIPT>
wddx.js is the WDDX library for JavaScript. It is available as
part of the WDDX SDK at http://www.wddx.org/.
- $wddx->string( 'Just a bunch of text...' )
- This creates a WDDX string object. Strings contain 8 bit characters, can
be any length, and should not include embedded nulls. However, control
characters and characters that have special meaning for XML (like <,
>, and &) are safely encoded for you.
- $w_string->as_scalar
- This returns the value of the WDDX::String as a Perl scalar.
- $wddx->number( 3.14159 )
- This creates a WDDX number object. Numbers are restricted to +/-1.7e308
and if you exceed these bounds this method dies with an error. Floating
point numbers are restricted to 15 digits of accuracy past the decimal. If
you exceed this then the number is truncated to 15 digits with a warning.
If you pass a non-numeric scalar to this, then it is simply treated as a
number: Perl will attempt to translate it, will probably use zero, and
will issue a warning.
- $w_number->as_scalar
- This returns the value of the WDDX::Number as a Perl scalar.
- $wddx->boolean( 1 )
- This creates a WDDX boolean object. It simply tests the argument in a
boolean context, so "0" and "" are false and anything
else is true.
- $w_boolean->as_scalar
- This returns the value of the WDDX::Boolean as a Perl scalar. True is
represented by 1 and false is represented by an empty string.
- $wddx->datetime
- This creates a WDDX Datetime object.
- $w_datetime->use_timezone_info( 1 )
- This sets or reads the flag that says whether to include the timezone info
(local hour and minute offset from UTC) in WDDX packets created from this
object. By default this is turned on for new objects. You can turn it off
by passing a false (but not undef) argument to this method.
When a WDDX::Datetime object is deserialized from a packet,
this method will indicate whether timezone information was present in
that packet.
- $w_datetime->as_scalar
- This returns the value of the WDDX::Datetime as a Perl scalar. It contains
the number of seconds since the epoch localized for the current machine
(like Perl's built-in "time" function).
This number can be passed into Perl's
"localtime" function.
- $wddx->null()
- This creates a WDDX null object. This is roughly the equivalent of
"undef" in Perl. It takes no
arguments.
- $w_datetime->as_scalar
- This simply returns "undef" (this was a
hard one to code :).
- $wddx->binary( $binary_data )
- This creates a WDDX binary object. It takes a scalar containing any data,
which will be base64 encoded before being serialized into the packet.
- $wddx->array( [ $wddx_obj1, $wddx_obj2, ... ] )
- This creates a WDDX::Array object. It takes a reference to an array
containing data objects. You must construct a WDDX data object for each
element of an array before adding them to the array. WDDX::Arrays can
contain any other WDDX data type and do not need to be of a uniform type,
so one array can contain a WDDX::String, a WDDX::Number, and a
WDDX::Struct, for example.
If you need to create an array of uniform types, Perl's
built-in "map" function makes this
easy. If you have a standard Perl array called
@array, you can generate a WDDX::Array of
WDDX::String objects like this:
my @obj_array = map $wddx->number( $_ ), @array;
my $wddx_array = $wddx->array( \@obj_array );
If you need to serialize more complicated array structures,
refer to "array2wddx" in the UTILITY
METHODS section.
- $wddx_array->as_arrayref()
- This returns a reference to a Perl array. Every element in the WDDX::Array
is recursively deserialized to Perl data structures. Only WDDX::Recordsets
remain as WDDX data objects.
- $wddx_array->get_element( $i )
- $wddx_array->get( $i )
- This allows you to get an element of a WDDX::Array as a data object
instead of having it deserialized to Perl.
- $wddx_array->set( $i => $wddx_obj );
- This allows you to set an element in a WDDX::Array. Note that
$wddx_obj should be a WDDX data object of some
type.
- $wddx_array->splice( $offset, $length, $wddx_obj1, $wddx_obj2, ...
);
- $wddx_array->splice( $offset, $length );
- $wddx_array->splice( $offset );
- This allows you to insert or delete elements in a WDDX::Array using the
syntax of Perl's built-in "splice"
function.
- $wddx_array->length();
- This returns the number of elements in the WDDX::Array object.
- $wddx_array->push( $wddx_obj1, $wddx_obj2, ... );
- This will push the given elements onto the WDDX::Array object.
- $wddx_array->pop();
- This will pop the last element off the WDDX::Array object and return
it.
- $wddx_array->unshift( $wddx_obj1, $wddx_obj2, ... );
- This will unshift the given elements onto the WDDX::Array object.
- $wddx_array->shift();
- This will shift the first element off the WDDX::Array object and return
it.
- $wddx->struct( { key1 => $wddx_obj1, key2 => $wddx_obj2, ... }
)
- $wddx->hash ( { key1 => $wddx_obj1, key2 => $wddx_obj2, ... }
)
- This creates a WDDX::Struct object. To WDDX, a struct is simply what Perl
refers to as a hash (or associative array). These two methods are aliases
so you can use whichever name you prefer.
There are no restrictions on keys, but values must be WDDX
data types. Just like with WDDX::Arrays, you have to create a WDDX data
type for each value you want to insert into a WDDX::Struct.
Here's how to use Perl's built-in
"map" function to generate a
WDDX::Struct if all of your values have the same data type. If you have
a standard Perl hash called %hash, you can
generate a WDDX::Struct of WDDX::String objects like this:
my %obj_hash = map { $_ => $wddx->number( $hash{$_} } keys %hash;
my $wddx_hash = $wddx->hash( \@obj_hash );
If you need to serialize more complicated hash structures,
refer to "hash2wddx" in the UTILITY
METHODS section.
- $wddx_array->as_hashref()
- This returns a reference to a Perl hash. Every element in the hash is
recursively deserialized to Perl data structures. Only WDDX::Recordsets
remain as data objects.
- $wddx_hash->get_element( $key );
- $wddx_hash->get( $key );
- This allows you to get an element of a WDDX::Struct as a data object
instead of having it deserialized to Perl.
- $wddx_hash->set( $key => $wddx_obj );
- This allows you to set a key/value pair in a WDDX::Struct. Note that
$wddx_obj should be a WDDX data object of some
type.
- $wddx_hash->delete( $key );
- This allows you to delete a key from a WDDX::Struct.
- $wddx_hash->keys();
- This will return a list of keys for the WDDX::Struct object or the number
of keys (if called in a scalar context).
- $wddx_hash->values();
- This will return a list of values for the WDDX::Struct object or the
number of values (if called in a scalar context). Note that each one of
these values should be a WDDX data object of some type.
- $wddx->recordset( [ NAME_A, NAME_B, ... ], [ TYPE_A, TYPE_B, ... ], [
DATA ] )
- This creates a WDDX::Recordset object. Recordsets hold tabular data. There
is no corresponding data type in Perl, but it corresponds with the type of
output you would receive from a SQL query.
The first argument when constructing a recordset should be a
reference to an array containing the names of each of the fields. The
second argument an reference to an array containing the types of each of
the fields. Field types must be simple, so the valid types are
"string", "number", "boolean", or
"datetime". The last argument is an optional reference to an
array of arrays -- in other words a table of data. Note that this table
of data contains plain old Perl scalars; you should not create WDDX
objects for each value as you would for an array or a hash.
$wddx_rec = $wddx->recordset( [ NAME_A, NAME_B, ... ],
[ TYPE_A, TYPE_B, ... ],
[ [ $val_a1, $val_b1, ... ],
[ $val_b1, $val_b2, ... ],
...
] )
This is simple to use with DBI:
$data = $dbh->selectall_arrayref( "SELECT NAME, AGE FROM TABLE" ) or
die $dbh->errstr;
$wddx_rec = $wddx->recordset( [ "NAME", "AGE" ],
[ "string", "number" ],
$data );
Recordsets that are within arrays or hashes are not
automatically deserialized for you when you deserialize the array or
hash. They remain as recordset objects. You can use the methods below to
access the data.
Note: It is possible to receive a packet for a recordset that
does not contain any records. In WDDX, the data type for each field is
determined by looking at how the data in the field has been tagged; so
if there is no data, then there is no data type information. Thus if you
deserialize an empty recordset packet, add data to the resulting
recordset object, and attempt to serialize it back into a packet, you
will get an error because WDDX.pm will not know what data type to assign
to the data you added. To avoid this, you should call the types()
method to set the data types before you serialize a recordset object
that was created by deserializing a packet. (If this explanation makes
no sense, reread it a few times; if it still doesn't make sense, email
me and let me know. :)
- $wddx_rec->names
- Returns a reference to an array of the field names. You can also pass a
reference to an array to set the names.
- $wddx_rec->types
- Returns a reference to an array of the field data types. You can also pass
a reference to an array to set the data types.
- $wddx_rec->table
- Returns a reference to an array of rows, each containing an array of
fields. You can also pass a reference to an array to set all the data at
once.
- $wddx_rec->num_rows
- Returns the number of rows.
- $wddx_rec->num_columns
- Returns the number of columns (or fields in a row).
- $wddx_rec->get_row( $row_num )
- Takes an row index (0 base) and returns a reference to an array for that
row.
- $wddx_rec->add_row( [ ARRAY ] )
- Takes a reference to an array and adds this row to the bottom of the
rows.
- $wddx_rec->del_row( $row_num )
- Takes a row index and deletes that row.
- $wddx_rec->set_row( $row_num, [ ARRAY ] )
- Takes a row index and a reference to an array. It replaces that row with
this new array.
- $wddx_rec->get_column( $col_name )
- Takes a column name or index (0 base) and returns a reference to an array
for that column.
- $wddx_rec->add_column( 'NAME', 'TYPE', [ ARRAY ] )
- Takes a column name, type, and a reference to an array and adds the column
to the end of the columns.
- $wddx_rec->set_column( 'NAME', [ ARRAY ] )
- Takes a column name or index (0 base) and a reference to an array.
Replaces the column with the values from this array.
- $wddx_rec->del_column( $name )
- Takes a column name or index (0 base) and deletes the column.
- $wddx_rec->get_element( $col_name, $row_num )
- Takes a column name or index (0 base) and row number and returns the value
of the intersecting cell.
- $wddx_rec->set_element( $col_name, $row_num, 'New value' )
- Takes a row number, a column number, and a value and sets the value of the
intersecting cell.
- $wddx_rec->get_field( $row_num, $col_num )
- DEPRECATED! Takes a row number and column number and returns the value of
the intersecting cell.
This method is deprecated. Because WDDX often refers to
columns in a recordset as fields, this method name may be confusing. It
has been replaced by get_element() and will be removed in a
future version.
- $wddx_rec->set_field( $row_num, $col_num, 'New value' )
- DEPRECATED! Takes a row number, a column number, and a value and sets the
value of the intersecting cell.
This method is deprecated. Because WDDX often refers to
columns in a recordset as fields, this method name may be confusing. It
has been replaced by set_element() and will be removed in a
future version.
These methods make it easier to go from Perl to WDDX data objects and vice
versa.
- $wddx->wddx2perl( $wddx_obj );
- This takes a WDDX data object and returns a scalar if it is a simple data
type, an array reference if it is an array, a hash reference if it is a
struct, and a WDDX::Recordset object if it is a recordset.
- $wddx->scalar2wddx( $scalar, [ $type ] );
- This method takes a scalar and a data type and returns the scalar as a
WDDX data object of the given type. Type should be one of the simple WDDX
data types (i.e. string, number, boolean, datetime, null, or binary), and
if it is not supplied, then string is assumed.
This method is convenient if you have the type stored in a
variable, since it avoids you having to do a bunch of if/else statements
to call the corresponding data object constructor.
- $wddx->array2wddx( $arrayref, [ $coderef ] );
- $wddx->hash2wddx( $hashref, [ $coderef ] );
- These methods attempt to provide a way for you to generate complex WDDX
data types from complicated Perl structures. In their simplest form, they
will generate a corresponding WDDX data object by serializing all scalars
as strings. This may be sufficient for your needs, but it likely will not.
Thus, these methods also allow you to determine the type for each scalar.
To do so, you must provide a reference to a sub.
Your sub will be called for each value within the array or a
hash you supply, as well as each value within any nested arrays or
hashes. Thus your sub may need to support both hashes and arrays.
If your sub is called within an array, it will receive the
following arguments:
1. the index of the current element
2. the value of the current element
3. the text "ARRAY"
If your sub is called within a hash, it will receive the
following arguments:
1. the key of the current pair
2. the value of the current pair
3. the text "HASH"
You must return the type of the data object to construct (e.g.
"number") or a false value if you want to let the element
continue to the next rule. The rules for converting elements into WDDX
data objects are as follows:
1. If the element is already a WDDX data object, then it
is left alone.
2. Your subroutine is called (if provided). If a true value is not
returned, then we skip to rule 3. If you return an invalid data type, then
this method "die"s with an error. If you
return a valid data type then:
a. If the current element is a scalar then this element
is converted to a WDDX data object of the type you specified.
b. If the current element is a reference to a hash or an array,
then this hash or array is converted to a WDDX data object with each element
having the type you specified (this applies to all nested arrays and hashes
too).
3. If the current element is a reference to a hash or an array
then "wddx2array" or
"wddx2hash" is called on it and your sub
(if provided) propagates.
4. Any scalars that have not been handled by a previous rule are
treated as strings.
Here is an example. Assume that you have the following data
structure in Perl:
$weather_data = {
title => "Weather Conditions",
region => "San Francisco Bay Area",
current => {
temp => 72,
sky => "mostly clear",
precip => undef,
wind => 12
},
tomorrow => {
temps => [ 62 => 75 ],
sky => "partly cloudy",
precip => undef,
winds => [ 5 => 10 ]
}
};
To convert this to a WDDX object you could create a handler and
use it to create a WDDX object like this:
$type_sub = sub {
my( $name, $val, $mode ) = @_;
! defined( $val ) and return "null";
$name =~ /temp/ and return "number";
$name =~ /wind/ and return "number";
};
my $wddx_weather = $wddx->hash2wddx( $weather_data, $type_sub );
Then you can easily serialize the WDDX object to a packet:
$WDDX::INDENT = " ";
print $wddx->serialize( $wddx_weather );
This prints:
<wddxPacket version='1.0'>
<header/>
<data>
<struct>
<var name='tomorrow'>
<struct>
<var name='temps'>
<array length='2'>
<number>0</number>
<number>1</number>
</array>
</var>
<var name='precip'>
<null/>
</var>
<var name='winds'>
<array length='2'>
<number>0</number>
<number>1</number>
</array>
</var>
<var name='sky'>
<string>partly cloudy</string>
</var>
</struct>
</var>
<var name='title'>
<string>Weather Conditions</string>
</var>
<var name='current'>
<struct>
<var name='wind'>
<number>12</number>
</var>
<var name='precip'>
<null/>
</var>
<var name='temp'>
<number>72</number>
</var>
<var name='sky'>
<string>mostly clear</string>
</var>
</struct>
</var>
<var name='region'>
<string>San Francisco Bay Area</string>
</var>
</struct>
</data>
</wddxPacket>
Of course, the handler you construct will vary depending on each
particular data structure.
I pulled the examples out of here when I realized that this POD was over 50
screenfuls on a standard term! For more lengthy examples, please visit
http://www.scripted.com/wddx/ or http://www.wddx.org/.
WDDX does not support 16 bit character sets (at least not without encoding them
as binary objects).
Every element of data must be encoded as an object. This increases
memory usage somewhat, and it also means any data you transfer must fit in
memory.
This is actually a non-bug: XML::Parser untaints data as it parses
it. This is dangerous. WDDX.pm retaints the data it receives from
XML::Parser so you should be safe if you are running in taint mode. Note:
WDDX.pm uses $0 to retaint data, so if you untaint
$0 then any subsequent WDDX.pm data will be
untainted too. Taint is explained perlsec.
Nate Weiss, the man behind the WDDX SDK, has been an especially huge help.
David Medinets started an earlier version of a Perl and WDDX
module available at http://www.codebits.com/wddx/.
The following people have helped provide feedback, bug reports,
etc. for this module:
Thomas R. Hall
David J. MacKenzie
Jon Sala
Wolfgang ???
James Ritter
Miguel Marques
Vadim Geshel
Adolfo Garcia
Sean McGeever
Allie Rogers
Ziying Sherwin
Origianally by Scott Guelich <scott@scripted.com>, now maintained by Andy
Lester "<andy@petdance.com>".
Visit the GSP FreeBSD Man Page Interface. Output converted with ManDoc. |