如何将XML元素A添加到没有A作为子元素的元素B.

时间:2013-06-15 04:40:05

标签: perl xml-parsing

我正在处理类似于xml的gpx文件。这是该文件的摘录:

<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>添加到那些不包含它的<time>元素中?

在读取文件时,如果XML节点不包含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(); } ,则会产生错误:

$time
  

无法在...

的未定义值上调用方法“textContent”

如何让它变得更聪明?也就是说,如果它遇到未定义的<time>,它会将{{1}}写入gpx文件,并且不会发生错误。

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:5)

  • 使用XML解析器。 XML::LibXML在这里会很好。
  • 遍历所有<trkpt>个节点:

    for my $node ( $xml->findnodes( '//trkpt') ) { ... }
    
  • 使用existsfindnodes用于旧版XML::LibXML)来检测<time>节点是否存在:

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