Kamal Kishore
Kamal Kishore

Reputation: 325

dynamically create url from hash ref in perl

I want to create url from input hash ref. Suppose I give hash ref as

my $input_hash_ref = {
        '1' => 'A',
        '2' => 'B',
        '3' => {
                '4' => {
                        '5' => {
                                '6' => [
                                        'ice cream','drink'
                                        ],
                                '7' => 'large'
                        }
                 '8' => 'perl'
                 '9' =>  'rosy'
                },
          '10'=>'june'
        },


};

Then this is to be converted as

1=A&2=B&3.4.5.6=ice cream|drinks&3.4.5.7=large&3.8=perl&3.9=rosy&10=june

Help needed.

Upvotes: 1

Views: 792

Answers (3)

mpapec
mpapec

Reputation: 50647

use URI::Escape;
sub serial {
  my ($h, $p) = @_;

  return join "&", map {
    my $v = $h->{$_};
    my $ref = ref($v);
    my $isH = $ref eq "HASH";
    my $pp = join ".", grep defined, $p, $_;

    $v = $isH ? serial($v,$pp)
       : $ref ? join("|", map uri_escape($_), @$v)
       : uri_escape($v);

    $isH ? $v : "$pp=$v";
  }
  sort keys %$h;
}

my $input_hash_ref = {
        '1' => 'A',
        '2' => 'B',
        '3' => {
                        '4' => {
                                '5' => {
                                        '6' => [
                                                'ice cream','drink'
                                                ],
                                          '7' => 'large'
                                          }
                                     },
                  },
        '8' => 'june'
};
print serial($input_hash_ref);

output

1=A&2=B&3.4.5.6=ice cream|drink&3.4.5.7=large&8=june

Upvotes: 1

Kent Fredric
Kent Fredric

Reputation: 57354

I just have to say, you shouldn't be composing query strings with a naive implementation with join.

use URI;
use URI::QueryParam;
my $u = URI->new("","http");

Then you can simply:

$u->query_param_append("1" => "A", "2" => "B", ....);

Or even

$u->query_form_hash( %somedata );

Note that this does not automatically deal with your custom schema for serialized nesting, but it does guarantee that you'll emit a valid query string that any server will understand.

Though you can also use a Perl Module to convert from a deeply-nested Hash to a Flat hash and back again:

And you can use this to convert between formats on both sides.

Example usage:

use strict;
use warnings;
use utf8;

use Data::SplitSerializer;
use Data::Dump qw(pp);
use URI;
use URI::QueryParam;

my $input_hash = {
        '1' => 'A',
        '2' => 'B',
        '3' => {
                '4' => {
                        '5' => {
                                '6' => [
                                        'ice cream','drink'
                                        ],
                                '7' => 'large'
                        }
                },
        },
        '8' => 'june',
        '9' => "Challenging & Value",
};

my $flattened = Data::SplitSerializer->new( path_style => 'DZIL' )->serialize($input_hash);
pp $flattened;

my $uri = URI->new("http://example.com/thing?");
$uri->query_form_hash( $flattened );

printf "%s\n", $uri;

my $copy = URI->new( $uri . "" ); # simulate getting it server side
my $copy_hash = $copy->query_form_hash;
pp $copy_hash;

my $deep = Data::

SplitSerializer->new( path_style => 'DZIL' )->deserialize($copy_hash);
pp $deep;

Example Output:

{
  "1" => "A",
  "2" => "B",
  "3.4.5.6[0]" => "ice cream",
  "3.4.5.6[1]" => "drink",
  "3.4.5.7" => "large",
  "8" => "june",
  "9" => "Challenging & Value",
}
http://example.com/thing?9=Challenging+%26+Value&3.4.5.6%5B1%5D=drink&2=B&8=june&3.4.5.6%5B0%5D=ice+cream&1=A&3.4.5.7=large
{
  "1" => "A",
  "2" => "B",
  "3.4.5.6[0]" => "ice cream",
  "3.4.5.6[1]" => "drink",
  "3.4.5.7" => "large",
  "8" => "june",
  "9" => "Challenging & Value",
}
{
  1 => "A",
  2 => "B",
  3 => { 4 => { 5 => { 6 => ["ice cream", "drink"], 7 => "large" } } },
  8 => "june",
  9 => "Challenging & Value",
}

Upvotes: 3

Denis Ibaev
Denis Ibaev

Reputation: 2520

sub c {
    my ($v, $p) = @_;
    my $r = ref($v);
    return map { c($v->{$_}, $p ? $p . '.' . $_ : $_) } keys(%$v) if $r eq 'HASH';
    return $p . '=' . join('|', @$v) if $r eq 'ARRAY';
    return $p . '=' . $v;
}

say(join('&', c($input_hash_ref)));

Upvotes: 0

Related Questions