2013-06-15 2 views
2

Я обрабатываю файл gpx, который напоминает xml. Вот выдержка из этого файла:Как добавить элемент XML A в элемент B, который не имеет A в качестве дочернего элемента

<trkpt lat="3.1398377" lon="101.6937661"> 
    <ele>0.0</ele> 
    <time>2013-01-01T00:00:00.000Z</time> 
    <name>Position 1</name> 
</trkpt> 

<trkpt lat="3.1250538" lon="101.6783237"> 
    <ele>0.0</ele> 
    <name>Position 460</name> 
</trkpt> 

Как вы можете видеть, некоторые из <trkpt> элементов содержат <time> элемент, а некоторые нет. Как добавить <time> к тем <trkpt> элементам, которые не содержат его?

Хотя файл читается, это приводит к ошибке, если узел XML не содержит <time>:

foreach $points ($root->getElementsByTagName('trkpt')) { 
    my($lat) = $points->findvalue('@lat'); 
    my($lon) = $pints->findvalue('@lon'); 
    my($time) = $points->getElementsByTagName('time')->[0]->textContent(); 
    my($pointName) = $points->getElementsByTagName('name')->[0]->textContent(); 
} 

Невозможно вызвать метод «TextContent» на неопределенное значение в ...

Как я могу сделать его более умным? То есть, если он встречает undefined $time, он будет записывать <time> в файл gpx, и ошибка не возникает.

+1

В вашем файле есть несколько элементов 'trkpt', или вы показываете два файла в вашем примере? – Borodin

+0

Да, есть много элементов 'trkpt'. Что я хочу сделать, вставьте элемент '

+0

ОК, тогда как большой файл? 'XML :: Twig' может быть лучшим вариантом. – Borodin

ответ

6

В зависимости от размер вашего файла, который вы можете захотеть, либо с XML::LibXML, который считывает весь XML-документ в память, или XML::Twig, что позволит обрабатывать XML как поток и минимизировать используемую память.

Для целей тестирования я добавил корневой элемент <root> для входных данных, чтобы сделать его хорошо сформированным XML, как этот

<root> 
    <trkpt lat="3.1398377" lon="101.6937661"> 
    <ele>0.0</ele> 
    <time>2013-01-01T00:00:00.000Z</time> 
    <name>Position 1</name> 
    </trkpt> 
    <trkpt lat="3.1250538" lon="101.6783237"> 
    <ele>0.0</ele> 
    <name>Position 460</name> 
    </trkpt> 
</root> 

Это решение с использованием XML::LibXML

use strict; 
use warnings; 

use XML::LibXML; 

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

for my $trkpt ($doc->findnodes('/*/trkpt')) { 
    unless ($trkpt->exists('time')) { 
    my ($ele) = $trkpt->findnodes('ele'); 
    my $time = $doc->createElement('time'); 
    $time->appendTextNode('0.0'); 
    $trkpt->insertAfter($time, $ele); 
    } 
} 

print $doc->toString(1); 

и здесь эквивалент usiong XML::Twig

use strict; 
use warnings; 

use XML::Twig; 

my $twig = XML::Twig->new(
    twig_roots => { trkpt => \&trkpt }, 
    twig_print_outside_roots => 1, 
    pretty_print => 'indented' 
); 
$twig->parsefile('trkpt.xml'); 

sub trkpt { 
    my ($twig, $trkpt) = @_; 
    unless ($trkpt->has_child('time')) { 
    my $time = XML::Twig::Elt->new(time => '0.0'); 
    my $ele = $trkpt->first_child('ele'); 
    $time->paste('after', $ele); 
    } 
    $twig->flush; 
} 
+0

Да, я был почти там, я не знал, что «insertAfter» когда-либо существовал. ЛОЛ. Большое спасибо. В самом деле. – momokjaaaaa

5
  • Использовать XML-парсер. XML::LibXML здесь будет хорошо.
  • перебирает все <trkpt> узлов:

    for my $node ($xml->findnodes('//trkpt')) { ... } 
    
  • Использование exists (или findnodes для старых версий XML::LibXML), чтобы обнаружить, если <time> узла существует:

    if $node->exists('./time') { ... } 
    
+0

что если ' узла не существует, как я могу написать'

+0

как сразу написать «

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