2013-06-16 4 views
1

Я пробовал XML Simple, но из-за того, что он просто считывает XML в хеш-образ, выход бесполезен при запуске против DTD. Выучил это с трудом.Использование XML :: LibXML для поиска и замены определенных частей XML-файла CableLabs 1.0

Итак, я принял XML::LibXML, самое смешное, что было труднее всего выполнить с XML::Simple, были самыми легкими. Однако я обнаружил, что некоторые из простых вещей, которые можно сделать в XML::Simple, оказались невозможными (с моим недостатком понимания DOM и некоторыми путаными поведением с XML::LibXML).

Так вот образец XML:

<Metadata> 
     <ADI Name="movie" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 
    <Metadata> 
     <ADI Name="photo" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 
    <Metadata> 
     <ADI Name="poster" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 

Примечание: Я упростил это для использования в этом сообщении.

Поэтому в основном я должен использовать Name поле в <ADI> теге, чтобы подтвердить, что я нахожусь в правильной области DOM, чтобы внести изменения в атрибут Value в <App_Data> тега, кто есть Name является ChangeMe.

Это фрагмент кода, который я придумал ... и неудачно потерпел неудачу.

#!/usr/bin/perl 

use strict; 
use XML::LibXML; 

my $xml2 = XML::LibXML->new(); 
my $data = $xml2->parse_file("adi.xml"); 
my $movie; 
my $photo; 
my $poster; 

foreach my $test ($data->findnodes('//Metadata')) { 
    if ($test->findvalues('./ADI/@Name[.="movie"]')){ 
     $movie = 1; 
     undef $photo; 
     undef $poster; 
    } 
    elsif ($test->findvalues('./ADI/@Name[.="photo"]')){ 
     undef $movie; 
     $photo = 1; 
     undef $poster; 
    } 
    elsif ($test->findvalues('./ADI/@Name[.="poster"]')){ 
     undef $movie; 
     undef $photo; 
     $poster = 1; 
    } 
} 

У меня нет ничего кроме этого, потому что это не сработает. Я получаю сообщение об ошибке что-то вдоль линий

Can't locate object method "findvalues" via package "XML::LibXML::Element" 

В качестве бонуса к этому вопросу, что, если бы я хотел, чтобы полностью удалить <Metadata> (и все дети) для тех, которые содержатся фото и/или плакат?

+0

'UNDEF $ вар,' это специальная версия '$ Var = UNDEF;', что приводит к Perl, чтобы сделать дополнительную работу. Используйте последний. – ikegami

+0

Непонятно, что вы намерены делать с '' как только вы идентифицируете тип «Метаданные». Как это будет выглядеть после изменения? – doubleDown

ответ

3

Дайте это попробовать для начала.

#!/usr/bin/perl 

use strict; 
use XML::LibXML; 

my $xml2 = XML::LibXML->new(); 
my $data = $xml2->parse_file("adi.xml"); 

foreach my $test ($data->findnodes('//Metadata')) { 
    if ($test->findnodes('./ADI/@Name[.="movie"]')){ 
     print "movie\n"; 
    } 
    elsif ($test->findnodes('./ADI/@Name[.="photo"]')){ 
     print "photo\n"; 
    } 
    elsif ($test->findnodes('./ADI/@Name[.="poster"]')){ 
     print "poster\n"; 
    } 
} 

Нет findvalues метод. То, что вы хотите сделать, это использовать findnodes, который вернет вам список узлов, соответствующих выражению XPath. После этого вы можете перебирать список и извлекать любые данные, которые вам нужны, так же, как вы уже делаете для Metadata.

Кроме того, я предполагаю, что ваш XML-файл имеет один элемент на уровне корневого уровня. Я использовал модифицированную версию ниже, чтобы проверить приведенный выше код.

<root> 
    <Metadata> 
     <ADI Name="movie" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 
    <Metadata> 
     <ADI Name="photo" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 
    <Metadata> 
     <ADI Name="poster" /> 
     <App_Data Name="Something I don't care about" value="who cares" /> 
     <App_Data Name="Something I don't care about as well" value="who cares" /> 
     <App_Data Name="ChangeMe" Value="" /> 
    </Metadata> 
</root> 

Я нахожу this cheatsheet полезной для библиотеки Libxml Perl.

2
  • Где вы нашли findvalues? Docs:

    @nodes = $node->findnodes($xpath_expression); 
    $result = $node->find($xpath); 
    print $node->findvalue($xpath); 
    
  • Почему так много применений .?

    ./ADI/@Name[.="movie"] 
    

    , вероятно, следует

    ADI[@Name="movie"] 
    
  • У вас есть более чем один элемент метаданных, но установить переменные, основываясь только на последней.

  • Вы не должны использовать три разные переменные для хранения одной части информации.


#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::LibXML qw(); 

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

for my $metadata ($doc->findnodes('//Metadata')) { 
    my ($adi_type) = $metadata->find('ADI/@Name') 
     or next; 

    my ($app_data) = $metadata->find('App_Data[@Name="ChangeMe"]'); 

    if ($adi_type eq 'movie') { 
     ... 
    } 
    elsif ($adi_type eq 'photo') { 
     ... 
    } 
    elsif ($adi_type eq 'poster') { 
     ... 
    } 
} 

Или вы можете даже использовать:.

my ($movie_adi) = $doc->findnodes('//Metadata[ADI/@Name="movie"]'); 
my ($movie_app_data) = $movie_adi->findnodes('App_Data[@Name="ChangeMe"]'); 
... 

my ($photo_adi) = $doc->findnodes('//Metadata[ADI/@Name="photo"]'); 
my ($photo_app_data) = $photo_adi->findnodes('App_Data[@Name="ChangeMe"]'); 
... 

my ($poster_adi) = $doc->findnodes('//Metadata[ADI/@Name="poster"]'); 
my ($poster_app_data) = $poster_adi->findnodes('App_Data[@Name="ChangeMe"]'); 
... 
2

Многое может быть сделано в выражении XPath, чтобы найти узлы, которые вы заинтересованы в

Эта программа будет делать то, что вы просите. Я добавил корневой элемент <root> к вашим данным, чтобы превратить его в хорошо сформированный XML-документ.

use strict; 
use warnings; 

use XML::LibXML; 

my $doc = XML::LibXML->load_xml(location => 'adi.xml', no_blanks => 1); 

for my $metadata ($doc->findnodes('//Metadata')) { 
    if ($metadata->findnodes('ADI[@Name = "movie" or @Name = "photo"]')) { 
    $metadata->parentNode->removeChild($metadata); 
    } 
} 

print $doc->toString(1); 

выход

<?xml version="1.0"?> 
<root> 
    <Metadata> 
    <ADI Name="poster"/> 
    <App_Data Name="Something I don't care about" value="who cares"/> 
    <App_Data Name="Something I don't care about as well" value="who cares"/> 
    <App_Data Name="ChangeMe" Value=""/> 
    </Metadata> 
</root> 
+0

Ницца. Mayhap '$ metadata-> unbindNode();' более сжатый способ отсоединения узла? – doubleDown