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

hi monks,

this is my first attempt at trying to create some xml output from a cgi file...
it seems there must be an easier way than what i've done, so i'm posting my code for your comments/criticisms
my long term aim is to write a swell forecast extension for my firefox browser
thanks in advance for any replies.
#!/usr/bin/perl use strict; use warnings; use LWP::Simple qw(get); my $url = "http://www.oceanoutlook.com.au/table/perth.html"; my $source = get($url); use HTML::Tree; my $tree = HTML::Tree->new(); $tree->parse($source); my (@tds) = $tree->look_down( '_tag', 'td' ); my @headers; my @data; my $month; my $i = 0; foreach (@tds) { $i++; next if ($i < 5); # The first 4 headers we dont need next if ($i == 16); # Bad HTML... skip it next if ($i == scalar(@tds)); # The last one we dont need if (($i >= 5) && ($i <= 15)) { # These are the headers we want my $header = $_->as_text; if ($i == 5) { $month = $_->as_text; $header = "Date"; } # end-if $header = "Swell Direction" if ($header eq "Dir"); push(@headers, $header); } # end-if if ($i > 16) { # This is the data push(@data, $_->as_text); } # end-if } # end-foreach # Output to xml use XML::Writer; my $writer = new XML::Writer(); $writer->startTag("doc", class => "simple"); $writer->dataElement( 'title', "Swell Forecast"); $writer->dataElement( 'month', $month); my $count = -1; for ($i=0;$i<scalar(@data);$i++) { $count++; if ($count == 0) { $writer->startTag( "prediction", date => $data[$i]); $writer->dataElement( 'title', $data[$i]); $i++; $count++; } # end-if if ($count == 2) { $writer->startTag( "swell_height" ); $writer->dataElement( 'unit', 'meters'); } # end-if if ($count == 5) { $writer->startTag( "peak_period" ); $writer->dataElement( 'unit', 'seconds'); } # end-if if ($count == 8) { $writer->startTag( "wind" ); $writer->dataElement( 'unit', 'knots'); } # end-if $writer->dataElement( 'item', $data[$i], name => $headers[$count]); if ($count == 4) { $writer->endTag( "swell_height" ); } # end-if if ($count == 7) { $writer->endTag( "peak_period" ); } # end-if if ($count == 10) { $writer->endTag( "wind" ); } # end-if if ($count == scalar(@headers)-1) { $writer->endTag(); $count = -1; } # end-if } # end-for $writer->endTag(); $writer->end(); exit;
I do have one question:
does the xml need to be written to a file or can it be directly outputed from the cgi script? when i try to output from the cgi and view via firefox i get the premature headers error, but the same xml code pasted into an .xml file renders fine in firefox.
cheers,
reagen

Replies are listed 'Best First'.
Re: creating xml from cgi
by ikegami (Patriarch) on Mar 08, 2007 at 07:02 UTC

    The server is expecting a CGI script, but your script doesn't follow the CGI protocol. Like the error message says, you're not sending the appropriate header. The safest thing to do is to use the CGI module.

    use CGI qw( :cgi ); print(header(-type => 'application/xml')); ... print XML here ...

    The above basically amounts to:

    print("Content-Type: application/xml\n"); print("\n"); ... print XML here ...
Re: creating xml from cgi
by hangon (Deacon) on Mar 08, 2007 at 07:00 UTC

    Do the methods you're using send a valid http header with your direct output? This error is being sent by your web server, which needs to receive the header when serving content from cgi. Web browsers don't need the header when reading from a file.

Re: creating xml from cgi
by GrandFather (Saint) on Mar 08, 2007 at 08:25 UTC

    One trick you can use is array slicing to clean up the element extraction. Then splicing each group of predictions from the data cleans up the logic of the generation loop somewhat:

    use strict; use warnings; use LWP::Simple qw(get); my $url = "http://www.oceanoutlook.com.au/table/perth.html"; my $source = get($url); use HTML::Tree; my $tree = HTML::Tree->new(); $tree->parse($source); my @tds = $tree->look_down( '_tag', 'td' ); my $month = $tds[4]->as_text (); my @headers = ('Date', map {$_->as_text} @tds[5 .. 14]); my @data = map {$_->as_text} @tds[16 .. $#tds - 1]; $_ eq 'Dir' and $_ = "Swell Direction" for @headers; # Output to xml use XML::Writer; my $writer = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 4); $writer->startTag("doc", class => "simple"); $writer->dataElement( 'title', "Swell Forecast"); $writer->dataElement( 'month', $month); while (@data >= @headers) { my ($date, $dir, @values) = splice @data, 0, @headers; $writer->startTag ("prediction", date => $date); $writer->dataElement ('title', $date); $writer->dataElement ('item', $dir, name => $headers[1]); $writer->startTag ("swell_height"); $writer->dataElement ('unit', 'meters'); $writer->dataElement ('item', $values[$_], name => $headers[$_ + +2]) for 0 .. 2; $writer->endTag(); $writer->startTag ("peak_period"); $writer->dataElement ('unit', 'seconds'); $writer->dataElement ('item', $values[$_], name => $headers[$_ + +2]) for 3 .. 6; $writer->endTag(); $writer->startTag ("wind"); $writer->dataElement ('unit', 'knots'); $writer->dataElement ('item', $values[$_], name => $headers[$_ + +2]) for 7 .. 9; $writer->endTag(); $writer->endTag(); # Close prediction } $writer->endTag(); # Close doc $writer->end(); exit;

    DWIM is Perl's answer to Gödel