Обработка нескольких документов XML в одном файле с помощью Perl - PullRequest
2 голосов
/ 06 октября 2011

Отредактировано: Извините, я неправильно набрал 'name', когда имею в виду 'ref', а также включил полные атрибуты

У меня есть несколько xml-файлов, в одной строке которых содержится полный xml-документ. Примером может быть:

<Reqeusts>
    <WRRequest><Request domain="foo.com"><Rows><Row includeascolumn="n" interval="hour" ref="time" type="group"/><Row includeascolumn="n"  ref="domain_id" type="group"/><Row />...</Rows><Columns><Column ref="user_id"/><Column ref="country_id"/><Column ref="country_name"/>...</Columns></Request></WRRequest>
.
.
.
</Requests>

Есть также ряд атрибутов, которые я не включаю для ясности.

Я анализирую это, используя XML :: Parser & XML :: SimpleObject, которые прекрасно работают до определенного момента. Например, я просто распечатываю атрибуты каждого из элементов, которые работают, за исключением случаев, когда я пытаюсь распечатать атрибут ref элемента column. Затем я получаю ошибку «неинициализированная переменная». Код:

#!/usr/bin/perl
use warnings;
use diagnostics;
use XML::Parser;
use XML::SimpleObject;
use Cwd;


if ($ARGV[0] eq "") {
  die "usage: sumXML.pl <input file> \n";
}

my $fileName = $ARGV[0];

my $parser = new XML::Parser(Style => 'Tree');
my $xso = XML::SimpleObject->new( $parser->parsefile("$fileName") );


foreach my $wrRequest ($xso->child('WRRequests')->children('RWRequest')) {
  print "Client Name: " . $wrRequest->attribute('clientName') . "\n";
foreach my $xmlRequest ($wrRequest->child('REQUEST')) {
  print "Domain name: " . $xmlRequest->attribute('domain') . "\n";
  print "Service: " . $xmlRequest->attribute('service') . "\n";
  foreach my $xmlRow ($xmlRequest->child('ROWS')->children('ROW')) {
    print "Row Reference: " . $xmlRow->attribute('ref') . "\n";
  }
  foreach my $xmlColumn ($xmlRequest->child('COLUMNS')->children('COLUMN')) {
    print "Column Reference: " . $xmlColumn->attribute('ref') . "\n";
  }
 }
  print "\n";
}

Ответы [ 2 ]

1 голос
/ 07 октября 2011

Я не могу точно знать, как данные должны быть идеально организованы, но я нахожу XML :: Rules в таких ситуациях удобным. Если вы открыты для совершенно другого способа сделать это, например, (Я предполагаю, что 'ref' является ключом в каждой строке, имена столбцов должны храниться в порядке, и все, что вам нужно, это атрибут 'ref' и т. Д.):

use strict;
use warnings;

use Data::Dumper;
use XML::Rules;

my $xml = <<XML;
<Requests>
  <WRRequest>
    <Request domain="foo.com" service="SomeService">
      <Rows>
        <Row includeascolumn="n" interval="hour" ref="time" type="group"/>
        <Row includeascolumn="n"  ref="domain_id" type="group"/>
      </Rows>
      <Columns>
        <Column ref="user_id"/>
        <Column ref="country_id"/>
        <Column ref="country_name"/>
      </Columns>
    </Request>
  </WRRequest>
</Requests>
XML

my @rules = (
  Request => sub { delete $_[1]->{_content}; print Dumper $_[1]; return },
  Rows    => 'pass no content',
  Columns => 'pass no content',
  Row     => 'no content by ref',
  Column  => sub { '@'.$_[0] => $_[1]{ref} },
);

my $p = XML::Rules->new(
  rules => \@rules,
);
$p->parse($xml);

__END__
$VAR1 = {
          'Column' => [
                      'user_id',
                      'country_id',
                      'country_name'
                    ],
          'domain' => 'foo.com',
          'time' => {
                    'type' => 'group',
                    'includeascolumn' => 'n',
                    'interval' => 'hour'
                  },
          'domain_id' => {
                         'type' => 'group',
                         'includeascolumn' => 'n'
                       },
          'service' => 'SomeService'
        };
1 голос
/ 07 октября 2011

Ваш пример данных не анализируется (даже если вы удалите точки), поэтому он не является допустимым XML. Я не уверен, как выглядят ваши реальные данные, но это очень важно, чтобы найти проблему.

Я уверен, что нет ничего плохого в XML::Parser или XML::SimpleObject. Поэтому, пожалуйста, проверьте следующее:

  • Правильно ли вы написали элемент / атрибут (помните, что XML чувствителен к регистру )
  • Существует ли элемент / атрибут в действительности (например: есть ли у каждого REQUEST -элемента service -атрибут? У каждого ROW есть ref -атрибут?). Если они не существуют, вы должны либо отклонить входные данные, либо работать с имеющимися у вас данными. Это, конечно, зависит от ваших требований.
  • Необязательно: проверьте дерево XML-документа по DTD или XSD для проверки целостности данных. Это похоже на расширенную версию второго пункта.

На самом деле я потратил время на то, чтобы заставить его работать (просто изменив регистр имен элементов и слегка изменив ваши «данные примера»):

use strict;
use warnings;
use XML::Parser;
use XML::SimpleObject;
use Cwd;


my $inXML = join "", <DATA>;
print $inXML;

my $parser = new XML::Parser(Style => 'Tree');
my $xso = XML::SimpleObject->new( $parser->parse($inXML) );


foreach my $wrRequest ($xso->child('Requests')->children('WRRequest')) {
    print "Client Name: " . $wrRequest->attribute('clientName') . "\n";
    foreach my $xmlRequest ($wrRequest->child('Request')) {
        print "Domain name: " . $xmlRequest->attribute('domain') . "\n";
        print "Service: " . $xmlRequest->attribute('service') . "\n";
        foreach my $xmlRow ($xmlRequest->child('Rows')->children('Row')) {
            print "Row Reference: " . $xmlRow->attribute('ref') . "\n";
        }
        foreach my $xmlColumn ($xmlRequest->child('Columns')->children('Column')) {
            print "Column Reference: " . $xmlColumn->attribute('ref') . "\n";
        }
    }
    print "\n";
}


__DATA__
<Requests>
  <WRRequest clientName="foo">
    <Request service="fooService" domain="foo.com">
      <Rows>
        <Row includeascolumn="n" interval="hour" ref="time" type="group"/>
        <Row includeascolumn="n"  ref="domain_id" type="group"/>
      </Rows>
      <Columns>
        <Column ref="user_id"/>
        <Column ref="country_id"/>
        <Column ref="country_name"/>
      </Columns>
    </Request>
  </WRRequest>
</Requests>

Выход:

Client Name: foo
Domain name: foo.com
Service: fooService
Row Reference: time
Row Reference: domain_id
Column Reference: user_id
Column Reference: country_id
Column Reference: country_name

Я также протестировал его с несколькими WRRequest -элементами (копировать и вставить) - работал как шарм.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...