http://qs321.pair.com?node_id=872768

fullermd has asked for the wisdom of the Perl Monks concerning the following question:

I've had puzzlingly little luck finding this on my own, so I resort to the monastery...

I can't seem to find much existing code to build up data structures (rather than flat lists) from passed (GET/POST) CGI data. I know nobody wants to be seen holding up anything from That Other 'P' Web Language as an exemplar, but... well, there it is.

For a trivial example, going from an HTML form with data like:

<input type="text" name="foo{bar}" value="barval"> <input type="text" name="foo{baz}" value="bazval">

into something in perl like:

$foo = get_param('foo'); # $foo = { bar => 'barval', baz => 'bazval' }

CGI.pm and things roughly following its API provide ways (of greater or lesser convenience) of getting arrays of values. But finding something to build up hashes is an unrewarding search. The only thing I've managed to unearth is CGI::State, which is tightly tied to CGI.pm, is a little unsettling code- and known-limitation-wise, and has had 2 releases 3 days apart almost 10 years ago. Minor searching has turned up approximately 0 live uses of it. Doesn't encourage me to hitch my wagon to it...

Of course, I can just be the one that writes code to do it. I think highly enough of my ability for that. But I also think highly enough of Murphy that I'd rather not, and my Programmer's Laziness is in full-scale rebellion at the thought that this doesn't already exist. Surely I'm not actually just the second person in the last 15 years to want this?

Replies are listed 'Best First'.
Re: Building data structures from CGI params
by Corion (Patriarch) on Nov 21, 2010 at 09:48 UTC

    You mean you want to fetch the parameter list as a hash? It's right in the CGI documentation.

    Update Ah - now I see, you want to do some "structured" parameters. No, I'm not aware of something like that, but if you go for dots as delimiters, it's fairly easy to write yourself:

    my %params = $q->Vars; my @keys = keys %params; for my $k (sort @keys) { if (/[.]/) { my $v = delete $params{ $k }; my $level = \%params; my @items = split /[.]/, $k; my $key = pop @items; for (@items) { $level->{ $_ } ||= {}; $level = $level->{ $_ }; }; $level->{ $key } = $v; }; };

      No, for the example HTML I had:

      <input type="text" name="foo{bar}" value="barval"> <input type="text" name="foo{baz}" value="bazval">

      that would yield me

      $params = { "foo{bar}" => "barval", "foo{baz}" => "bazval" }

      I want

      $params = { foo => { bar => "barval", baz => "bazval", } }
      EDIT

      Pshaw, you update'd while I was typing :)

      Yes, it's not (in the simple case) that hard to code up. Handling params like foo{bar}[6]{baz}[2] gets a little more involved though. It's just complex enough that I'm confident I'll either make a mistake somewhere, or wind up with code with somewhat unpleasant performance characteristics in extremis. So I'd hoped (and it seems such an obvious thing to want that I'd expected) there was an existing module I could mooch off^W^Wutilize...

      Ah - now I see, you want to do some "structured" parameters. No, I'm not aware of something like that, but if you go for dots as delimiters, [...]

      Unfortunately (fortunately ;) this is perl, so we have to have multiple delimiters to be able to support both hashes and arrays. That makes things a little more complicated (not to mention that pesky error checking).

      Thanks to you (and Anonymous below) for the code drops. I wasn't really looking for "help me write this code" so much as "help me find the module that surely already exists to do it", but I'm continually impressed at the willingness of the Monks to go that extra mile.

      Sadly, none of us were successful at finding the existing module. I'm still shocked by that. Left me no choice but to go solve it.

      So I did, and the next time someone asks, we can tell them CGI::Struct.

      Annoyingly, I just got a mail about a test failure on 5.6.2. At a glance, it looks like it's just a problem 5.6 has with the test, not an actual code issue. Hrumph. Now I need to decide whether I want to fight through a 5.6 install somewhere to fix it...

        You forgot how CGI::Vars works, https://metacpan.org/module/CGI::Struct#Auto-arrays doesn't account for it

        #!/usr/bin/perl -- use strict; use warnings; use Data::Dump; use CGI; use CGI::Struct; my $q = CGI->new('row[]=row;row[]=row;row[]=row;row[]=your boat'); my %qVars = $q->Vars; my @errors; my $struct = build_cgi_struct \%qVars, \@errors; dd $q, \%qVars, $struct, \@errors; ### WORKAROUND for stupid Vars (must've written this line 100 times) %qVars = map { $_ => [ $q->param($_) ] } $q->param; dd \%qVars; $struct = build_cgi_struct \%qVars, \@errors; dd $struct, \@errors; __END__ ( bless({ ".charset" => "ISO-8859-1", ".fieldnames" => {}, ".iterator" => 2, ".parameters" => ["row[]"], "escape" => 1, "param" => { "row[]" => ["row", "row", "row", "your boat"] +}, "use_tempfile" => 1, }, "CGI"), { "row[]" => "row\0row\0row\0your boat" }, { row => ["row\0row\0row\0your boat"] }, [], ) { "row[]" => ["row", "row", "row", "your boat"] } ({ row => ["row", "row", "row", "your boat"] }, [])
Re: Building data structures from CGI params
by Anonymous Monk on Nov 21, 2010 at 11:02 UTC
    I know nobody wants to be seen holding up anything from That Other 'P' Web Language as an exemplar, but... well, there it is.

    What do they call this feature?

    Surely I'm not actually just the second person in the last 15 years to want this?

    To my knowledge, why yes, yes you are :D

    It seems to be common symptom among those who view cookies and forms as databases... that is not the way to manage session data :)

    I would investigate Data::FormValidator and CGI::FormBuilder as an alternative

      that is not the way to manage session data

      Quite. Luckily for me, I'm not trying to manage session data, I'm trying to manage form data :p

      A flat namespace is great when you're dealing with 3 or 4 bits of info conceptually at the same level. When you're dealing with 20 or 50 or 200, that are conceptually at varying levels and associations, not so much.

        But what do those other guys call this feature? Link?

        It vaguely reminds me of JSON::Path