diff options
author | Hakim Cassimally <hakim@mysociety.org> | 2014-03-13 16:56:02 +0000 |
---|---|---|
committer | Hakim Cassimally <hakim@mysociety.org> | 2014-10-16 16:56:26 +0000 |
commit | d1fee928f02dbc30d3a38b746155ce5b12be4a1b (patch) | |
tree | 5e8bdccbd69863e69098b9aa900c1e71745f8eb5 /perllib/Open311/Endpoint/Spark.pm | |
parent | 592f4c0ba0f822b55bb242cb12768ce771599d09 (diff) |
Open311 Endpoint
Subsystems include
* ::Spark encoding conventions for xml/json
* ::Schema using Rx to validate form of inputs and outputs,
including validation for, e.g., dates and CSV as part of Open311
Handles following paths:
* Open311 attributes for Service Definition
http://wiki.open311.org/GeoReport_v2#GET_Service_Definition
* POST service request
* GET Service Requests
* GET Service Request
Objects:
* ::Service
* ::Service::Request
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; |