diff options
Diffstat (limited to 'perllib/Open311/Endpoint/Spark.pm')
-rw-r--r-- | perllib/Open311/Endpoint/Spark.pm | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/perllib/Open311/Endpoint/Spark.pm b/perllib/Open311/Endpoint/Spark.pm new file mode 100644 index 000000000..ae179cecc --- /dev/null +++ b/perllib/Open311/Endpoint/Spark.pm @@ -0,0 +1,116 @@ +package Open311::Endpoint::Spark; +use Moo; +use Data::Visitor::Callback; + +=head1 NAME + +Open311::Endpoint::Spark - transform from canonical data-structure to XML or JSON + +=head1 SUMMARY + +The Open311 docs discuss the Spark convention, to transform between XML and JSON. + + http://wiki.open311.org/JSON_and_XML_Conversion#The_Spark_Convention + +These options seem fragile, and require starting with the verbose XML form, +which isn't really natural in Perl. Instead, we'll start with a standard +Perl data structure, with a single extra hash wrapper, and will: + + * for JSON, remove the outside hash wrapper + + * for XML, for arrays, insert an extra layer with the singular name: + (this is the way XML::Simple knows how to do this nesting) + +So: + + # FROM + { + foo => { + bars => [ 1, 2, 3 ] + } + } + + # JSON (note the 'foo' has been removed + { + bars: [ + 1, + 2, + 3 + ] + } + + # XML intermediate + { + foo => { + bars => { + bar => [ 1, 2, 3 ] + } + } + } + + # XML result + <foo> + <bars> + <bar>1</bar> + <bar>2</bar> + <bar>3</bar> + </bars> + </foo> + +=cut + +sub process_for_json { + my ($self, $data) = @_; + if (ref $data eq 'HASH' and scalar keys %$data == 1) { + my $inner = $data->{ (keys %$data)[0] }; + $data = $inner if ref $inner; + } + return $data; +} + +sub process_for_xml { + my ($self, $data) = @_; + + # NB: in place mutation + _process_for_xml($data); + return $data; +} + +# NB: in place mutation +sub _process_for_xml { + my $data = shift; + return unless ref $data; + + if (ref $data eq 'HASH') { + while ( my ($k, $v) = each %$data) { + if (ref $v eq 'ARRAY') { + my $singular = _singularize($k); + # add extra layer + $data->{$k} = { + $singular => $v, + }; + } + _process_for_xml($v); + } + } + elsif (ref $data eq 'ARRAY') { + for my $item (@$data) { + _process_for_xml($item); + } + } +} + +my %singular_map = ( + service_requests => 'request', +); +sub _singularize { + my $name = shift; + return $singular_map{ $name } + || do { + # strip final 's' if present + $name =~ s/s$//; + return $name; + }; +} + +1; |