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

I am trying to filter through a large XML file, pull out the elements I need within the value range restricted by the IF statement, and export them to CSV. However, I am a newb to programming and this is my first exposure to Perl, so I get syntax errors on lines 14 and 15 and probably doing other things wrong too. Please help me straighten this out!

Here's my code:

#! /usr/bin/perl -w use strict; use XML::Twig; my $twig= new XML::Twig( TwigHandlers => {town => \&town} ); if( my $file= $ARGV[0]) {$twig->parsefile( 'datafile_towns.xml'); $twig->flush;} sub town { my ($twig, $town) = @_; if (my $townx($town->first_child('location')->first_child('mapx'))>5 +00){ print $town->first_child('towndata')->first_child_text('townname') +, ',', $town->first_child('player')->first_child_text('allianc +eticker'), ',', $town->first_child('player')->first_child_text('playern +ame'), ',', $town->first_child('towndata')->first_child_text('townn +ame'), ',', $town->first_child('location')->first_child_text('mapy' +), ',', $town->first_child_text('location')->first_child_text(' +mapx'), print "\n"; }; $twig->flush; }

And here's a snippet of XML:

<towns> <server> <name>servername</name> <servername>servername</servername> </server> <town> <location> <mapx>383</mapx> <mapy>-1815</mapy> <terraintype id="9">terraintype</terraintype> <terrainoveralltype id="4">terraintype</terrainoveralltype> </location> <player> <playername id="1">somedude</playername> <playerrace>Human</playerrace> <playeralliance> <alliancename id="18">somealliance</alliancename> <allianceticker>AL</allianceticker> <alliancetaxrate>0.01</alliancetaxrate> </playeralliance> </player> <towndata> <townname id="1">sometown</townname> <foundeddatetime>date</foundeddatetime> <population>1</population> <iscapitalcity>1</iscapitalcity> <isalliancecapitalcity>1</isalliancecapitalcity> </towndata> </town>

Replies are listed 'Best First'.
Re: TWIGging XML to CSV: what's wrong with my syntax?
by choroba (Cardinal) on Apr 10, 2015 at 21:07 UTC
    Using XML::XSH2, a wrapper around XML::LibXML:
    stream :f datafile_towns.xml :F /dev/null select town { if (location/mapx > 500) { echo :n :s (towndata/townname) , (player/playeralliance/allian +ceticker) ; echo :n :s , (player/playername) , (towndata/townname) , (loca +tion/mapy) ; echo :s , (location/mapx); rm . ; } }

    On MSwin, replace /dev/null with NUL.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: TWIGging XML to CSV: what's wrong with my syntax?
by toolic (Bishop) on Apr 10, 2015 at 20:44 UTC
    This gets past the syntax errors:
    use warnings; use strict; use XML::Twig; my $twig = XML::Twig->new( twig_handlers => { town => \&town }, ); $twig->parsefile('datafile_towns.xml'); sub town { my ($twig, $town) = @_; if ($town->first_child('location')->first_child_text('mapx') > 300 +) { print $town->first_child('towndata')->first_child_text('townna +me'); print "\n"; } } __END__ sometown

    Key points:

    • "twig_handlers"
    • No "my $townx" and extra parens
    • Need closing "towns" in your snippet
    • I changed 500 to 300 just to see output

      Thanks toolic! I went with your modification and extended it a bit to include a couple more conditions.

      use warnings; use strict; use XML::Twig; my $source_file = 'datafile_towns.xml'; my $out_file = 'illytowns.xml'; my $twig = XML::Twig->new( twig_handlers => { town => \&town }, ); $twig->parsefile($source_file); open (my $fh_out, '>', $out_file) or die "unable to open '$out_file' f +or writing: $!"; sub town { my ($twig, $town) = @_; my $townx = $town->first_child('location')->first_child('mapx')->t +ext; my $towny = $town->first_child('location')->first_child('mapy')->t +ext; if (($townx > 556) and ($towny > -2502) and ($towny < -2214)) { print $town->first_child('towndata')->first_child_text('townna +me'); print "\n"; } $twig->print($fh_out); $twig->purge; }
      I've added a file handle to print the data to a file rather than to STOUT; however, it is not working correctly, as instead of getting a file with a list of town names I get a a full set of tags for 1 XML element (probably the last element in the XML file). The STDOUT generated by the print statement inside the twig is correct.
Re: TWIGging XML to CSV: what's wrong with my syntax?
by runrig (Abbot) on Apr 10, 2015 at 21:24 UTC
    And yet another way, using XML::Rules:
    use warnings; use strict; use XML::Rules; my @attribs = qw( townname allianceticker playername townname mapy mapx ); my @rules = ( town => sub { my $town = $_[1]; return if $town->{mapx} <= 500; print join(",", @$town{@attribs}), "\n"; return; }, 'player,towndata,location,playeralliance' => 'pass no content', _default => 'content', ); my $xr = XML::Rules->new( rules => \@rules ); $xr->parsefile('tmp.xml');

      Thanks Runrig! This looks very nice. I've installed XML::Rules and tried running this code but I get an error:

      no element found at line 1, column 0, byte 0 at /usr/local/share/perl/5.18.2/XML/Rules.pm line 745.
        That sounds more like your XML file is invalid...did you use my hard-coded filename above instead of your actual file name?