在使用SAX解析XML文件时,如何创建与元素值等效的模块对象?

时间:2009-11-18 16:20:16

标签: xml perl sax

我有不同的模块,如Author.pm,BillingPeriod.pm,Offer.pm,PaymentMethod.pm等现在在sax中,每当我点击结束元素标签我想创建模块的对象,它等同于元素值。

我怎样才能做到这一点?

例如,如果正在解析XML文件和sax解析器命中的end元素,那么它应该创建Offer.pm的对象,类似地,如果sax解析器命中了end元素标记,那么它应该创建Author.pm的对象

代码

XML:books.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--Sample XML file generated by XMLSpy v2009 sp1 (http://www.altova.com)-->
<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>

萨克斯: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")

例如,在下面的示例中,假设sax正在点击Offer end element tag,那么创建Offer.pm的对象

我想创建模块的对象,例如,当sax命中Offer元素标记的end元素时,Offer.pm

  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分析器处理程序模块: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--;
}

问题:使用SAX解析XML文件时,如何创建与元素值等效的模块对象?

2 个答案:

答案 0 :(得分:2)

一般来说,您在SAX中需要做的是:

  1. 在处理start_element时创建工作区,以保存最终需要填充对象的嵌套标记中的值。
  2. 在end_element上,实例化对象
  3. 或者,您可以在start_element上实例化(空)对象,然后处理嵌套的字符()和start_element()事件以填充它。在所有情况下,您都需要跟踪当前的处理状态,以便在遇到每个元素类型时知道如何处理。您还需要一个全局上下文堆栈,它跟踪层次结构中的逻辑位置并指向当前对象/工作区。

    这是指向处理这些问题的introduction的指针。

答案 1 :(得分:1)

我厌倦了一遍又一遍地看到相同的XML,我决定给你一条鱼。为了您自己的利益,您需要付出更多努力来解释您的问题。我的意思是,即使您发布的XML也包含错误。

以下代码有一些非常规方面。他们在那里,所以你必须弄清楚在将你的代码作为你自己的代码传递给你的老板/客户之前发生了什么。

#!/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)-->
<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>

输出:

C:\Temp> hui
String by String was published on 1967-08-13