package Simple::SAX::Serializer::Handler;
use strict;
use warnings;
use vars qw(@EXPORT_OK %EXPORT_TAGS $VERSION);
use base 'Exporter';
$VERSION = 0.02;
@EXPORT_OK = qw(array_handler array_of_objects_handler hash_handler hash_item_of_child_value_handler hash_of_object_array_handler hash_of_array_handler ignore_node_handler root_object_handler object_handler custom_array_handler);
%EXPORT_TAGS = (all => \@EXPORT_OK);
=head1 NAME
Simple::SAX::Serializer::Handler - Collections of the mapping handlers for Simple::SAX::Serializer.
=head1 SYNOPSIS
use Simple::SAX::Serializer::Handler ':all';
use Simple::SAX::Serializer;
my $xml = Simple::SAX::Serializer->new;
$xml->handler('node', array_handler());
...
my $result = $xml->parse_string($xml_content);
=head1 DESCRIPTION
Collections of the mapping handlers for Simple::SAX::Serializer.
=head1 EXPORT
array_handler
array_of_objects_handler
hash_handler
hash_item_of_child_value_handler
hash_of_object_array_handler
hash_of_array_handler
ignore_node_handler
custom_array_handler
object_handler
root_object_handler by ':all' tag
=head2 METHODS
=over
=cut
=item array_handler
Takes optionally storage key to the array(by default the element name).
Returns handler for transforming nodes value into array
my $xml_content = <<XML;
<?xml version="1.0"?>
<root >
<node>value1</node>
<node>value2</node>
<nodeA>value1</nodeA>
<nodeA>value2</nodeA>
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('node', array_handler());
$xml->handler('nodeA', array_handler());
$xml->handler('root', sub {
my ($self, $element, $parent) = @_;
my $attributes = $element->attributes;
my $result = $element->children_result;
});
my $result = $xml->parse_string($xml_content);
#transforms $result to {{node => ['value1' ,'value2'], nodeA => ['value1', 'value2']}
=cut
sub array_handler {
my ($parent_key) = @_;
sub {
my ($self, $element, $parent) = @_;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
my $array = $result->{$key} ||= [];
push @$array, $element->value(1);
};
}
=item array_of_objects_handler
Returns handler for transforming nodes attribute into array of the objects.
Takes class name or constructor code reference as parameter,
array ref of the required attributes, hash_ref of the optional attributes,
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<object1 attr1="1" />
<object1 attr1="2" />
<object2 attr1="3" />
<object2 attr1="4" />
</root>
XML
package Object1;
sub new {
my $class = shift;
bless {@_}, $class
};
package Object2;
sub new {
my $class = shift;
bless {@_}, $class
};
sub object2{
Object2->new(@_);
}
my $xml = Simple::SAX::Serializer->new;
$xml->handler('object1', ::array_of_objects_handler('Object1', ['attr1']));
$xml->handler('object2', ::array_of_objects_handler(\&object2, ['attr1']));
$xml->handler('root', sub {
my ($self, $element, $parent) = @_;
my $attributes = $element->attributes;
$element->children_result;
});
my $result = $xml->parse_string($xml_content);
#transforms $result to [
Object1->new(attr1 => 1),
Object1->new(attr1 => 2),
Object2->new(attr1 => 3),
Object2->new(attr1 => 4),
];
=cut
sub array_of_objects_handler {
my ($object_constructor, $required_attributes, $optional_attributes) = @_;
my $result;
if (ref($object_constructor) eq 'CODE') {
$result = sub {
my ($self, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_array_result;
push @$result, $object_constructor->(%$attributes, %$children_result);
}
} else {
$result = sub {
my ($self, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_array_result;
push @$result, $object_constructor->new(%$attributes, %$children_result);
}
}
$result;
}
=item hash_of_object_array_handler
Returns handler for transforming nodes attribute into array of the objects, that
is stored as hash item of the parent node.
Takes class name or constructor code reference as parameter,
array ref of the required attributes, hash_ref of the optional attributes,
storage key to the array(by default the element name).
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<object1 attr1="1" />
<object1 attr1="2" />
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('object1', hash_of_object_array_handler('Object1', ['attr1']));
$xml->handler('root', sub {
my ($self, $element, $parent) = @_;
my $attributes = $element->attributes;
$element->children_result;
});
my $result = $xml->parse_string($xml_content);
#transforms $result to [
Object1->new(attr1 => 1),
Object1->new(attr1 => 2),
Object2->new(attr1 => 3),
Object2->new(attr1 => 4),
];
=cut
sub hash_of_object_array_handler {
my ($object_constructor, $required_attributes, $optional_attributes, $parent_key) = @_;
my $result;
if (ref($object_constructor) eq 'CODE') {
$result = sub {
my ($self, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
my $array = $result->{$key} ||= [];
push @$array, $object_constructor->(%$attributes, %$children_result);
}
} else {
$result = sub {
my ($self, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
my $array = $result->{$key} ||= [];
push @$array, $object_constructor->new(%$attributes, %$children_result);
}
}
$result;
}
=item hash_of_array_handler
Returns handler for transforming nodes attribute into array of the hash items, that
is stored as hash item of the parent node.
Takes array ref of the required attributes, hash_ref of the optional attributes,
storage key to the array(by default the element name).
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<object1 attr1="1" />
<object1 attr1="2" />
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('object1', hash_of_array_handler(['attr1']));
$xml->handler('root', root_object_handler('Root'));
my $result = $xml->parse_string($xml_content);
#converts result to
Root->new(object1 => [{attr1 => 1}, {attr1 => 2}]);
=cut
sub hash_of_array_handler {
my ($required_attributes, $optional_attributes, $parent_key) = @_;
sub {
my ($self, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
my $array = $result->{$key} ||= [];
push @$array, {%$attributes, %$children_result};
}
}
=item hash_handler
Takes optionally parent storage key to the hash item (by default the element name).
optionally attribute name for the hash key (by default name).
Returns handler for transforming node into hash item.
Key of the hash is evaluated from the name node's attribute.
Value of the hash is evaluated from the node's value.
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<node name="key1">value1</node>
<node name="key2">value2</node>
<node name="key3">value3</node>
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('node', hash_handler());
$xml->handler('root', root_object_handler('Root'));
my $result = $xml->parse_string($xml_content);
#transforms $result to Root->new(node => {key1 => 'value1', key2 => 'value2', key3 => 'value3'})
=cut
sub hash_handler {
my ($parent_key, $hash_key_attribute) = @_;
sub {
my ($self, $element, $parent) = @_;
my $hash_key = $hash_key_attribute || 'name';
$element->validate_attributes([$hash_key]);
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
my $hash = $result->{$key} ||= {};
$hash->{$attributes->{$hash_key}} = $element->value(1);
};
};
=item hash_item_of_child_value_handler
Takes optionally parent storage key to the hash item (by default the element name).
Returns handler for transforming child node value into hash value
Key of the hash is evaluated from current element name.
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<objects>
<object1 attr1="1" />
<object1 attr1="2" />
</objects>
<objects2>
<object2 attr1="3" />
<object2 attr1="4" />
</objects2>
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('objects', hash_item_of_child_value_handler());
$xml->handler('object1', array_of_objects_handler('Object1'));
$xml->handler('objects2', hash_item_of_child_value_handler());
$xml->handler('object2', array_of_objects_handler('Object2'));
$xml->handler('root', sub {
my ($self, $element, $parent) = @_;
my $attributes = $element->attributes;
$element->children_result;
});
my $result = $xml->parse_string($xml_content);
#transforms $result to {
objects => [Object1->new(attr1 => 1), Object1->new(attr1 => 2),],
objects2 => [Object2->new(attr1 => 3), Object2->new(attr1 => 4),]
};
=cut
sub hash_item_of_child_value_handler {
my ($parent_key) = @_;
sub {
my ($self, $element, $parent) = @_;
my $columns = $element->children_result;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
$result->{$key} = $columns;
}
}
=item root_object_handler
Returns handler for transforming root node into an object,
Takes class name or constructor code reference, optionally code reference to customize return values,
array ref of the required attributes, hash_ref of the optional attributes,
my $xml_content = <<XML;
<?xml version="1.0"?>
<root attr1="1" attr2="2">
<objects>
<object1 attr1="1" />
<object1 attr1="2" />
</objects>
<object2>3</object2>
<object2>4</object2>
</root>
XML
{
package Root;
sub new {
my $class = shift;
bless {@_}, $class
};
}
my $xml = Simple::SAX::Serializer->new;
$xml->handler('objects', hash_item_of_child_value_handler());
$xml->handler('object1', array_of_objects_handler('Object1'));
$xml->handler('object2', array_handler());
$xml->handler('root', root_object_handler('Root'));
#transforms $result to
Root->new(
attr1 => 1, attr2 => 2,
objects => [Object1->new(attr1 => 1), Object1->new(attr1 => 2),],
object2 => [3, 4]
);
=cut
sub root_object_handler {
my ($object_constructor, $code, $required_attributes, $optional_attributes) = @_;
my $result;
if (ref($object_constructor) eq 'CODE') {
$result = sub {
my ($self, $element, $parent) = @_;
my $args = $self->root_args;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $attributes = $element->attributes;
my $children_result = $element->children_result || {};
my $result = $object_constructor->(
%$attributes,
%$children_result,
%$args
);
$code ? $code->($result) : $result;
}
} else {
$result = sub {
my ($self, $element, $parent) = @_;
my $args = $self->root_args;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $attributes = $element->attributes;
my $children_result = $element->children_result || {};
my $result = $object_constructor->new(
%$attributes,
%$children_result,
%$args
);
$code ? $code->($result) : $result;
}
}
$result;
}
=item ignore_node_handler
=cut
sub ignore_node_handler {
sub {
my ($self, $element, $parent) = @_;
};
}
=item custom_array_handler
Returns handler for transforming roo node to object,
Takes hash ref as custom result storage, optionally array ref of the required attributes, hash_ref of the optional attributes,
custom storage key to the hash item (by default the element name).
This options allows parsing only part of the xml document.
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<node name="key1" attr1="1" />
<node name="key2" attr1="2" />
</root>
XML
my $result = {};
my $xml = Simple::SAX::Serializer->new;
$xml->handler('root', ignore_node_handler());
$xml->handler('node', custom_array_handler($result, ['name', 'attr1'], {optional_attr1 => undef, attr2 => '2'}, 'my_key'));
#or without attributes validation
$xml->handler('node', custom_array_handler($result, undef, undef, 'my_key'));
$xml->parse_string($xml_content);
#transforms $result to ,{ my_key => [
{name => 'key1', attr1 => '1', optional_attr1 => undef, attr2 => 2},
{name => 'key2', attr1 => '2', optional_attr1 => undef, attr2 => 2}
]
};
=cut
sub custom_array_handler {
my ($custom_storage, $required_attributes, $optional_attributes, $storage_key) = @_;
sub {
my ($this, $element, $parent) = @_;
$element->validate_attributes($required_attributes, $optional_attributes)
if ($required_attributes || $optional_attributes);
my $attributes = $element->attributes;
my $children_result = $element->children_result || {};
my $key = $storage_key || $element->name;
my $array = $custom_storage->{$key} ||= [];
push @$array, {%$attributes, %$children_result};
}
}
=item object_handler
Returns handler for transforming node into an object,
Takes class name or constructor code reference, optionally array ref of the required attributes, hash_ref of the optional attributes,
parent storage key to the hash item (by default the element name).
my $xml_content = <<XML;
<?xml version="1.0"?>
<root>
<object1 attr1="1" />
<object1 attr1="2" />
</root>
XML
my $xml = Simple::SAX::Serializer->new;
$xml->handler('object1', hash_of_object_array_handler('Object1', ['attr1']));
$xml->handler('root', root_object_handler('Root'));
my $result = $xml->parse_string($xml_content);
#transforms result to
Root->new(object1 => [Object1->new(attr1 => 1),Object1->new(attr1 => 2)]);
=cut
sub object_handler {
my ($object_constructor, $required_attributes, $optional_attributes, $parent_key) = @_;
my $result;
if (ref($object_constructor) eq 'CODE') {
$result = sub {
my ($self, $element, $parent) = @_;
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
$result->{$key} = $object_constructor->(%$attributes, %$children_result);
}
} else {
$result = sub {
my ($self, $element, $parent) = @_;
my $children_result = $element->children_result || {};
my $attributes = $element->attributes;
my $result = $parent->children_hash_result;
my $key = $parent_key || $element->name;
$result->{$key} = $object_constructor->new(%$attributes, %$children_result);
}
}
$result;
}
1;
__END__
=back
=head1 SEE ALSO
L<Simple::SAX::Serializer::Element>
=head1 COPYRIGHT AND LICENSE
The Simple::SAX::Serializer::Handler module is free software. You may distribute under the terms of
either the GNU General Public License or the Artistic License, as specified in
the Perl README file.
=head1 AUTHOR
Adrian Witas, adrian@webapp.strefa.pl
=cut
1;