user2642601 user2642601 - 6 months ago 35
Perl Question

Perl: XML to MySQL with LibXML

I'm pretty new to Perl and i'm writing a perl script to practice and i want to parse info from XML file to MySQL DB , but i'm stuck and i cannot find a way to import the data to MySQL.

Here is my Perl code:

#!/usr/local/bin/perl
use strict;
use warnings;
use diagnostics;
use XML::LibXML;
use DBI;
my $filename = 'test.xml';
my $dom = XML::LibXML->load_xml(location => $filename);
my $sport_id;
my $sport_name;
my $competition_id;
my $competition_name;
my $game_id;
my $game_start;
my $game_name;
my @values;
my $dbh = DBI->connect("dbi:mysql:parser:127.0.0.1", "root", "123qwe", { RaiseError => 1}) or die $DBI::errstr;
my $query = 'INSERT INTO sports (sport_id,sport_name,competition_id,competition_name,game_id,game_start,game_name) VALUES (?,?,?,?,?,?,?)';
my $sth = $dbh->prepare($query) or die "Prepare failed: " . $dbh->errstr();

foreach my $test ($dom->findnodes('//Sport')) {
print "\n";
$sport_id = $test->findvalue('./ID');
$sport_name = $test->findvalue('./Name');
$competition_id = $test->findvalue('./Competitions/Competition/ID');
$competition_name = $test->findvalue('./Competitions/Competition/Name');
$game_id = $test->findvalue('./Competitions/Competition/Games/ID');
$game_start = $test->findvalue('./Competitions/Competition/Games/Start');
$game_name = $test->findvalue('./Competitions/Competition/Games/Name');
#print "Sport ID: $sport_id\n";
#print "Sport Name: $sport_name\n";
#print "Competition ID: $competition_id\n";
#print "Competition Name: $competition_name\n";
#print "Game ID: $game_id\n";
#print "Game Start: $game_start\n";
#print "Game Name: $game_name\n";
#print "\n";
push @values, $sport_id,$sport_name,$competition_id,$competition_name,$game_id,$game_start,$game_name;
$sth->execute(@values) or die $dbh->errstr;
}


My XML:

<Sports>
<Sport>
<ID>1369527874</ID>
<Name>Virtual Football</Name>
<Competitions>
<Competition>
<ID>1374380502</ID>
<Name>Virtual Football. World - G.Devs Stadium</Name>
<Games>
<ID>1974885309</ID>
<Start>2016-05-11 12:21:00</Start>
<Name>New England Militia - St. Louis Racers</Name>
<ID>1974892839</ID>
<Start>2016-05-11 12:27:00</Start>
<Name>Las Vegas Rollers - Salt Lake Wrath</Name>
</Games>
</Competition>
</Competitions>
</Sport>
<Sport>
<ID>882</ID>
<Name>Darts</Name>
<Competitions>
<Competition>
<ID>1834852369</ID>
<Name>Darts. World - PDC European Tour Outright</Name>
<Games>
<ID>1895020486</ID>
<Start>2016-05-15 23:00:00</Start>
<Name>PDC European Tour. Outright</Name>
</Games>
</Competition>
</Competitions>
</Sport>
</Sports>


MySQL structure:

+------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+----------------+
| id | int(6) | NO | PRI | NULL | auto_increment |
| sport_id | varchar(255) | YES | | NULL | |
| sport_name | varchar(255) | YES | | NULL | |
| competition_id | varchar(255) | YES | | NULL | |
| competition_name | varchar(255) | YES | | NULL | |
| game_id | varchar(255) | YES | | NULL | |
| game_start | varchar(255) | YES | | NULL | |
| game_name | varchar(255) | YES | | NULL | |
+------------------+--------------+------+-----+---------+----------------+


If i remove the comment from the print lines the output will be as follow:

Sport ID: 1369527874
Sport Name: Virtual Football
Competition ID: 1374380502
Competition Name: Virtual Football. World - G.Devs Stadium
Game ID: 19748853091974892839
Game Start: 2016-05-11 12:21:002016-05-11 12:27:00
Game Name: New England Militia - St. Louis RacersLas Vegas Rollers - Salt Lake Wrath


Sport ID: 882
Sport Name: Darts
Competition ID: 1834852369
Competition Name: Darts. World - PDC European Tour Outright
Game ID: 1895020486
Game Start: 2016-05-15 23:00:00
Game Name: PDC European Tour. Outright


As you can see , the main problem is that i have multiple games and i cant seem to find a way to split them so i can import them to mysql.

Answer

I would reformulate what you're doing. It looks like your table is one row per game not one row per sport.

So you need an 'inner loop' to pick out the game IDs. Unfortunately - the're not grouped, so you need to do a 'next' sort of operation.

Using XML::Twig because I'm more familiar with it - something like this:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->parse( \*DATA );

foreach my $sport ( $twig->findnodes('//Sport') ) {
    my %fields;
    $fields{sport_id}         = $sport->findvalue('./ID');
    $fields{sport_name}       = $sport->findvalue('./Name');
    $fields{competition_id}   = $sport->findvalue('.//Competition/ID');
    $fields{competition_name} = $sport->findvalue('.//Competition/Name');
    foreach my $game ( $sport->findnodes('.//Games/ID') ) {
        $fields{game_id}    = $game->text;
        $fields{game_start} = $game->next_sibling->text;
        $fields{game_end}   = $game->next_sibling->next_sibling->text;
        print "Fields: ", join(
            ",",
            @fields{
                qw(sport_id sport_name
                    competition_id competition_name
                    game_id game_start game_end)
            }
            ),
            "\n";
    }    
}

(Pretty sure you can do much the same in XML::LibXML)

Comments