2013-09-17 3 views
0

У меня очень длинный xml, и я хочу обновить значение атрибута одного из тегов, который очень глубоко вложен, поэтому не хотите идти узлом по узлу. Также структура не совпадает по целевому узлу всегда, как можно увидеть ниже: Input XML является:Изменение значения атрибута тега xml с помощью perl

<Re> 
<Co Class="Parameter" ID="CSCP001" Status="Available"> 
<FileSpec URL="c://mine/testfiles/wln/c.txt"/> 
<CoOp Operation="Tag" SourceCS="RGB" SourceObjects="All"> 
<FileSpec Resource="SourceProfile" URL="c://mine/testfiles/wln/d.txt"/> 
</CoOp> 
</Co> 
<Ru Class="Parameter" ID="IDR002" PartIDKeys="Run" Status="Available"> 
<Ru EndOfDocument="true" Pages="0" Run="1" RunTag="First"> 
<La> 
<FileSpec URL="c://mine/testfiles/wln/e.txt"/> 
</La> 
</Ru> 
</Ru> 
</Re> 

и я хочу иметь вывода XML, как

<Re> 
<Co Class="Parameter" ID="CSCP001" Status="Available"> 
<FileSpec URL="d://yours/wln/c.txt"/> 
<CoOp Operation="Tag" SourceCS="RGB" SourceObjects="All"> 
<FileSpec Resource="SourceProfile" URL="d://yours/wln/d.txt"/> 
</CoOp> 
</Co> 
<Ru Class="Parameter" ID="IDR002" PartIDKeys="Run" Status="Available"> 
<Ru EndOfDocument="true" Pages="0" Run="1" RunTag="First"> 
<La> 
<FileSpec URL="d://yours/wln/e.txt"/> 
</La> 
</Ru> 
</Ru> 
</Re> 

Я попытался с помощью XML простой, xmllib но не в состоянии сделать необходимое. Я новичок в программировании perl.

use XML::LibXML qw(); 
use XML::LibXML; 
use Data::Dumper; 

my $xml = "a.txt"; 
my $xpath_expression = 'FileSpec'; 

my $parser = XML::LibXML->new(); 
my $doc = $parser->parse_file($xml) or warn "Could not"; 

my $parser1 = XML::LibXML::Element->new($xml); 


for my $FileSpec1 ($doc->getElementsByTagName('FileSpec')) 
{ 
print $FileSpec1; 
my $xpath = '$FileSpec1/@URL'; 
my ($attr) = $doc->findnodes($xpath);  
$attr->setValue('dfdsa'); 
my ($URL1) = $FileSpec1->findvalue('@URL'); 
print $URL1; 
} 

Я попытался использовать $ node-> setAttribute ($ aname, $ avalue); но это бросает исключения. Пожалуйста посоветуй.

ответ

1

Вы можете попробовать XML::Twig модуль. Он имеет параметр twig_handlers, который выбирает теги, которые вы хотите, и запускает обработчик. Переменная $_ по умолчанию имеет элемент и способ его set_att() позволяет изменить его значение легко:

#!/usr/bin/env perl 

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

my $new_url = q{d://yours/wln/d.txt}; 

my $twig = XML::Twig->new(
     twig_handlers => { 
       'FileSpec' => sub { $_->set_att('URL', $new_url) } 
     }, 
     pretty_print => 'indented', 
)->parsefile(shift)->print(); 

Выполнить это нравится:

perl script.pl xmlfile 

Это дает:

<Re> 
    <Co Class="Parameter" ID="CSCP001" Status="Available"> 
    <FileSpec URL="d://yours/wln/d.txt"/> 
    <CoOp Operation="Tag" SourceCS="RGB" SourceObjects="All"> 
     <FileSpec Resource="SourceProfile" URL="d://yours/wln/d.txt"/> 
    </CoOp> 
    </Co> 
    <Ru Class="Parameter" ID="IDR002" PartIDKeys="Run" Status="Available"> 
    <Ru EndOfDocument="true" Pages="0" Run="1" RunTag="First"> 
     <La> 
     <FileSpec URL="d://yours/wln/d.txt"/> 
     </La> 
    </Ru> 
    </Ru> 
</Re> 

EDIT: Mirod's версия указал в комментариях более эффективного синтаксического анализа с использованием twig_roots():

#!/usr/bin/env perl 

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

my $new_url = q{d://yours/wln/d.txt}; 

my $twig = XML::Twig->new(
     twig_roots => { 
       'FileSpec' => sub { $_->set_att('URL', $new_url); $_->flush } 
     }, 
     twig_print_outside_roots => 1, 
     pretty_print => 'indented', 
)->parsefile(shift); 
+1

если вы замените '' twig_handlers' на twig_roots', добавьте '$ _-> flush' в конце обработчика и добавить 'twig_print_outside_roots => 1' для конструктора, который становится' my $ twig = XML :: Twig-> new (twig_roots => {'FileSpec' => sub {$ _-> set_att ('URL', $ new_url) , $ _-. flush;}}, twig_print_outside_roots => 1, pretty_print => 'indented',) ', тогда файл будет анализироваться, но не полностью загружен в память, поэтому объем памяти должен быть минимальным. – mirod

+0

@mirod: Спасибо. Я не привык использовать 'twig_roots()', но это вариант, который нужно учитывать. Я добавил его в ответ, чтобы его было легче читать, но вы должны создать свой собственный ответ. Посоветуйте мне в этом случае удалить его. – Birei

+0

с использованием 'twig_roots' не требуется, если файл действительно большой и не помещается в память. В большинстве случаев вы отвечаете хорошо. Я просто прокомментировал это из-за «очень длинного XML» в вопросе. – mirod

4

Ваш код является слишком сложным. Вы не нуждаетесь в парсер, никаких элементов, просто найти ссылки и изменить их:

#!/usr/bin/perl 
use warnings; 
use strict; 

use XML::LibXML; 

my $xml = 'XML::LibXML'->load_xml(location => 'a.xml') ; 

for my $url ($xml->findnodes('//FileSpec/@URL')) { 
    my $value = $url->getValue; 
    $value =~ s{c://mine/testfiles}{d://yours}; 
    $url->setValue($value); 
} 

$xml->toFile('new.xml'); 
+0

Это не работает для меня. Я даже попробовал напечатать $ value после моего $ value = $ url-> getValue; но это ничего не возвращает. new.xml создается только со старыми деталями. – user2786324

+0

@ user2786324: он определенно работает с образцом XML, который вы предоставили в вопросе. Так вы используете другой XML для тестирования? –

+0

да, мой плохой ... это работает на образец xml. Я использую другой xml, который является довольно журнальным, но имеет аналогичную структуру. позвольте мне узнать. – user2786324

Смежные вопросы