Элемент синтаксического анализа (включая атрибут) и текстовые узлы XML-документа с использованием perl - PullRequest
0 голосов
/ 22 марта 2011

У меня есть следующий test.xml:

<root>
<A title="A1">
  <B title="B1">
   <C title="C1">
    <params>param=ABC1</params>
    <params>param=ABC2</params>
   </C>
  </B>
</A>
<D title="D1">
  <B title="B2">
   <C title="C2">        
     <params>param=DBC1</params>
     <params>param=DBC2</params>
   </C>
  </B>
</D>
</root>

Мне нужен Perl-код, чтобы проанализировать его и распечатать отчет как:

NdeName,  Attribute(s),  Param(s)
A          A1
B          B1
C          C1          param=ABC1 param=ABC2
D          D1
B          B2
C          C2          param=DBC1 param=DBC2  

Я пытался использовать getElementByTagName('param') и getNodeChilds и т. Д. ... безуспешно. Также я использовал модуль XML :: DOM .

Вот код:

 use XML::DOM;
 my $parser = new XML::DOM::Parser;
 my $doc = $parser->parsefile("test.xml");
 my @paramarray=();
 ParseXML($doc,"");

sub ParseXML{
 my $node = $_[0];
 my $indent = $_[1];
 my $title;

 if ($node == null) {
  return;
 }

 my $type = $node->getNodeType();
 if ($type == DOCUMENT_NODE) {
  ParseXML($node->getFirstChild(),"");
  break;
 }  

  if ($type == ELEMENT_NODE) {
  $numberAttributes =0;
  if ($node->getAttributes() !=null){
     $numberAttributes = $node->getAttributes()->getLength();
  }

 for ($loopIndex =0; $loopIndex<$numberAttributes; $loopIndex++) {
     $attribute = ($node->getAttributes())->item($loopIndex);
     if($attribute->getNodeName() eq "title"){
      $title = $attribute->getNodeValue();
     }
 }

 if ($node->getNodeName() eq "params"){
  foreach my $paramvar ($doc->getElementsByTagName("params")) {
     foreach my $child ($paramvar->getChildNodes) {
        push(@paramarray, $child->getData);
     }
  }
 }


 if ($node->getNodeName() ne "root") {
      print $node->getNodeName. ", $title, @paramarray\n";
      @paramarray=();
 } 

 my @childNodes = $node->getChildNodes()
 if (@childNodes != null){
   my $numberChildNodes = $#childNodes + 1;
   my $loopIndex;
  for ($loopIndex =0; $loopIndex<$numberChildNodes; $loopIndex++) {
       ParseXML($childNodes[$loopIndex],$indent);
  }
 }
 }

  if ($type == TEXT_NODE) {
    my  $nodeText = $node->getNodeValue();
  }

 }

Ответы [ 3 ]

2 голосов
/ 22 марта 2011

Вот пример кода, как выполнить задачу с помощью XML :: Twig :

use strict; use warnings;
use XML::Twig;

my $twig = XML::Twig->new( twig_handlers => {
    '//*[@title]' => sub {
        print join("\t",
            $_->gi,
            $_->att('title'),
            map { $_->trimmed_text } $_->findnodes('params')
        ), "\n";
    },
} );
$twig->parsefile('test.xml');
1 голос
/ 22 марта 2011

Я использую XML :: LibXML , так что вот решение, которое использует этот модуль.

use strict;
use warnings;

use XML::LibXML qw( );

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_file("test.xml");
my $root   = $doc->documentElement();

for my $node ($root->findnodes('//*[@title]')) {
    my $name   = $node->nodeName();
    my $title  = $node->getAttribute('title');
    my @params = map $_->textContent, $node->findnodes('params');
    printf("%-10s %-11s %s\n", $name, $title, join(' ', @params));
}

Обновление : Все еще XML :: LibXML, но на этот раз без XPath для простоты преобразования в XML :: DOM.

use strict;
use warnings;

use XML::LibXML qw( XML_ELEMENT_NODE );

sub find_params {
    my ($node) = @_;

    my @params;
    for my $child ($node->childNodes()) {
        next if $child->nodeType != XML_ELEMENT_NODE;
        next if $child->nodeName ne 'params';
        push @params, $child->textContent();        
    }

    return @params;    
}

sub visit {
    my ($node) = @_;
    return if $node->nodeType != XML_ELEMENT_NODE;

    if (my $title_node = $node->getAttributeNode('title')) {
        printf("%-10s %-11s %s\n",
            $node->nodeName(),
            $title_node->getValue(),
            join(' ', find_params($node)),
        );
    }

    visit($_) for $node->childNodes();
}

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_file("test.xml");
my $root   = $doc->documentElement();

visit($root);
1 голос
/ 22 марта 2011

Прежде всего, всегда начинайте с

use strict;
use warnings;

Это поймает много опечаток и глупых ошибок, которые вы можете сделать. Одна большая проблема, с которой вы столкнулись, заключается в том, что null не является термином Perl. Perl использует undef и определенную определенную функцию (хотя в этом случае вам может не потребоваться defined, поскольку undef имеет значение false, а объекты обычно имеют значение true).

Вот несколько исправленная версия вашего кода. Он по-прежнему не производит запрашиваемый вывод, но он ближе.

use strict;
use warnings;
use XML::DOM;

my $parser = XML::DOM::Parser->new;
my $doc = $parser->parsefile("test.xml");
my @paramarray;
ParseXML($doc,"");

sub ParseXML {
  my $node = $_[0];
  my $indent = $_[1];
  my $title;

  if (not $node) {
    return;
  }

  my $type = $node->getNodeType();
  if ($type == DOCUMENT_NODE) {
    ParseXML($node->getFirstChild(),"");
    return;           
  }

  if ($type == ELEMENT_NODE) {
    my $numberAttributes =0;
    if ($node->getAttributes()) {
      $numberAttributes = $node->getAttributes()->getLength();
    }

    for (my $loopIndex =0; $loopIndex<$numberAttributes; $loopIndex++) {
      my $attribute = ($node->getAttributes())->item($loopIndex);
      if ($attribute->getNodeName() eq "title") {
        $title = $attribute->getNodeValue();
      }
    }

    if ($node->getNodeName() eq "params") {
      foreach my $paramvar ($doc->getElementsByTagName("params")) {
        foreach my $child ($paramvar->getChildNodes) {
          push(@paramarray, $child->getData);
        }
      }
    } elsif ($node->getNodeName() ne "root") {
      print $node->getNodeName. ", $title, @paramarray\n";
      @paramarray=();
    }

    my @childNodes = $node->getChildNodes(); # was missing semicolon

    if (@childNodes) {
      my $numberChildNodes = $#childNodes + 1;
      my $loopIndex;
      for ($loopIndex =0; $loopIndex<$numberChildNodes; $loopIndex++) {
        ParseXML($childNodes[$loopIndex],$indent);
      }
    }
  }

  if ($type == TEXT_NODE) {
    my $nodeText = $node->getNodeValue();
    # Were you planning on doing something here?
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...