tags:

views:

386

answers:

2

I have different modules like Author.pm, BillingPeriod.pm, Offer.pm,PaymentMethod.pm etc. now in sax whenever I hit the end element tag I want to create object of module which is equivalent to element value.

How can I achieve this ?

For example if am parsing through XML file and sax parser hit's end element as than it should create object of Offer.pm, similarly if sax parser hit's end element tag as than it should create object of Author.pm

Code

XML: books.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--Sample XML file generated by XMLSpy v2009 sp1 (http://www.altova.com)--&gt;
<bks:books xsi:schemaLocation="urn:books Untitled1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bks="urn:books">
        <book id="String">
                <author>String</author>
                  <authorFirstName>String</authorFirstName>
                  <authorLastName>String</authorLastName>
                <title>String</title>
                   <titleNo>3</titleNo>
                <genre>String</genre>
                <offer>String</offer>
                <pub_date>1967-08-13</pub_date>
                <review>String</review>
                  <reviewsratings></reviewratings>
        </book>
</bks:books>

sax: perlsaxparsing.pl

#!usr/bin/perl -w

use XML::SAX::ParserFactory;
use MySaxHandler;
my $handler = MySaxHandler->new();
my $parser = XML::SAX::ParserFactory->parser(Handler => $handler);
$parser->parse_uri("books.xml")

For example in below example, assuming that sax is hitting Offer end element tag so am creating object of Offer.pm

I want to create object of modules, for e.g, Offer.pm in this case when sax hits end element of Offer element tag.

  package Offer;
    use strict;

    # This class depicts the product_offer details
    sub new {
        my $class = shift;
        my $self  = {
            _objectId        => shift,
            _price           => shift

        };
        bless $self, $class;
        return $self;
    }

    # Returns the ObjectID
    sub getObjectId {
        my ($self) = @_;
        return $self->{_objectId};
    }


    # Returns the Price
    sub getprice {
        my ($self) = @_;
        return $self->{_price};
    }

    # Check for undefined values and build a insert mapping table
    sub doPreInsetCheck() {
        my ($self) = @_;
        my %refTable;
        if ( defined $self->getObjectId == 1 ) {
            $refTable{'object_id'} = $self->getObjectId;
        }
        if ( defined $self->getprice == 1 ) {
            $refTable{'fk2_price'} = $self->getprice;
        }
        return %refTable;
    }

    # Returns the SQL Statement
    sub getSQLScript {
        my $tableName = 'product_offer';
        my ($self)    = @_;
        my $sqlOutput = "Insert into " . $tableName . "(";
        my %refTable  = $self->doPreInsetCheck();
        my @colNames  = keys %refTable;
        my $ctr;
        foreach ( $ctr = 0 ; $ctr < ( $#colNames + 1 ) ; $ctr++ ) {
            $sqlOutput .= $colNames[$ctr];
            if ( $ctr < $#colNames ) {
                $sqlOutput .= ",";
            }
        }
        $sqlOutput .= ") values (";
        my @colVals = values %refTable;
        foreach ( $ctr = 0 ; $ctr < ( $#colVals + 1 ) ; $ctr++ ) {
            $sqlOutput .= $colVals[$ctr];
            if ( $ctr < $#colVals ) {
                $sqlOutput .= ",";
            }
        }
        $sqlOutput .= ");";
        return $sqlOutput;
    }
    1;

SAX Parser Handler Module: MySaxHander.pm

sub end_element {
    my($self,$data) = @_;
    print "\t Ending element:".$data->{Name}."\n";
    my $obj = new Price("1","2","NL","ENUM","DESCRIPTION","2008-01-01  10:00:00","2009-01-01 10:00:00","2008-01-01 10:00:00","USER");
print $obj->getSQLScript."\n";
$in_books--;
}

Question: While Parsing through XML file using SAX, How can I create object of module which is equivalent to element value ?

+1  A: 

In general what you have to do in SAX is to:

  1. Create a work area while handling start_element, to hold the values from the nested tags that you will eventually need to populate the object.
  2. On end_element, instantiate the object

Or, you could instantiate the (empty) object on start_element and then handle nested characters() and start_element() events to populate it. In all cases you will need to keep track of the current processing state so you know what to do with each element type as you encounter it. You will also need a global context stack that tracks your logical position in the hierarchy and points to the current object/workarea.

Here's a pointer to an introduction that deals with these issues.

Jim Garrison
Can you point me to simple Perl example which illustrates this scenario ?
Rachel
Why would there be need of global context stack which tracks the logical position in the hierarchy and points to current object/workarea...I guess this algorithm will surely solve my issue...can you elaborate more on the algorithm so that I can get very clear understanding of it for implementing it
Rachel
Your start_element and end_element methods receive only the data pertaining to the current node. There has to be a structure external to these methods in which you can keep track of where you are at each event. Since XML is hierarchical this external structure is most easily managed as a stack, where you push context on each "start" and pop on each "end".
Jim Garrison
A: 

I am getting so tired of seeing the same XML over and over that I decided to give you a fish. For your own good, you need to put more effort into explaining your questions. I mean, even the XML you posted contains errors.

The code below has some unconventional aspects. They are there so that you will have to figure out what is going on before passing this code as your own to your boss/client.

#!/usr/bin/perl

package My::Book;
use strict; use warnings;

use base 'Class::Accessor::Faster';

 __PACKAGE__->follow_best_practice;

__PACKAGE__->mk_accessors(qw(
    id author authorFirstName authorLastName title titleNo
    genre offer pub_date review reviewsratings
));

package My::Handler;
use strict; use warnings;

{{

my ($current_element, $element_data);

sub new { bless $_[1] => $_[0] }

sub start_element {
    my ($self, $data) = shift;
    my ($el) = @_;

    if ( (my $local_name = $el->{LocalName}) eq 'book' ) {
        my $book = My::Book->new({
            id => $el->{Attributes}{'{}id'}{Value}
        });
        push @$self, $book;
    }
    elsif ( $local_name ne 'books' ) {
        $current_element = $el->{LocalName};
    }
    return;
}

sub characters {
    my ($self, $data) = @_;
    if ( defined $current_element ) {
        $element_data .= $data->{Data};
    }
    return;
}

sub end_element {
    my ($self, $el) = @_;

    unless ( (my $local_name = $el->{LocalName}) =~ /\Abooks?\z/ ) {
        my $accessor = "set_$local_name";
        $self->[-1]->$accessor($element_data);
    }
    $current_element = undef;
    $element_data = '';
    return;
}


}}

package main;
use strict; use warnings;

use XML::SAX;

my @books;

my $parser = XML::SAX::ParserFactory->parser(
    { Handler => My::Handler->new(\@books) },
);

$parser->parse_file(\*DATA);

for my $book ( @books ) {
    printf("%s by %s was published on %s\n",
        $book->get_title, $book->get_author, $book->get_pub_date
    );
}


__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<!--Sample XML file generated by XMLSpy v2009 sp1 (http://www.altova.com)--&gt;
<bks:books xsi:schemaLocation="urn:books Untitled1.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bks="urn:books">

<book id="String">
<author>String</author>
<authorFirstName>String</authorFirstName>
<authorLastName>String</authorLastName>
<title>String</title>
<titleNo>3</titleNo>
<genre>String</genre>
<offer>String</offer>
<pub_date>1967-08-13</pub_date>
<review>String</review>
<reviewsratings></reviewsratings>
</book>
</bks:books>

Output:

C:\Temp> hui
String by String was published on 1967-08-13
Sinan Ünür