如何使用重复的兄弟节点组导航XML

时间:2014-04-22 19:09:48

标签: xml xpath xml-parsing xml-libxml

我正在使用Perl和XML :: LibXML,我必须处理的XML看起来像这样:

<PARAM NAME = "A"><VALUE>1</VALUE>
<PARAM NAME = "B"><VALUE>3</VALUE>
<PARAM NAME = "C"><VALUE>43</VALUE>
<PARAM NAME = "A"><VALUE>6</VALUE>
<PARAM NAME = "B"><VALUE>3</VALUE>
<PARAM NAME = "C"><VALUE>13</VALUE>
.
.
.

我需要的输出基本上是:

A    B    C
1    3    43
6    3    13

我把文字节点名称放到这样的数组中:

my @attributes = (
    './PARAM[@NAME = "A"]/VALUE',
    './PARAM[@NAME = "B"]/VALUE',
    .
    .
);

然后使用findnodes()和findvalue()将这些xpath文字作为foreach循环中的参数,错误地尝试获取值的'set',以写入记录。 当然,findnodes()是错误的,因为它通过循环获得满足条件的所有节点(正如它应该做的那样),而findvalues()是错误的,因为它实际上做同样的事情,只是连接所有类似的 - 命名节点值。

由于这个文件的结构是这样的,我看不到捕获'A thru C'节点/值,写一条记录,然后重复...至少没有检查每个节点,看它是不是'最后一个'('C')。似乎我需要将其作为一个普通的旧文本文件处理,基本上。

3 个答案:

答案 0 :(得分:0)

你没有提供你正在使用的语言,但它似乎是perl。基本上,获取所有<VALUE/>个元素(分别是它们的文本节点)然后循环遍历它们,每次读取三个值。

有点像peroc的伪代码:

@attributes = xpath('//PARAM/VALUE');
for ($i = 0; i < length(@attributes); i += 3)
  push @records (@attributes[$i], @attributes[$i + 1], @attributes[$i + 2])

结果,你应该得到一个数组数组(当然你也可以返回一个哈希数组)。如果您只想要输出,请使用上面的模式,并调用printf而不是push

答案 1 :(得分:0)

这是我采取的一种方法:

foreach my $parameter ( $raid_group->findnodes('PARAM')) {
    my $name  = $parameter->findvalue('@NAME);
    my $value = $parameter->findvalue('VALUE');
    if ($name eq $first_name_in_set ){
        [do stuff]
    }
}

这是一个使用螺丝刀做凿子的情况我认为 - 权宜之计但不多。

答案 2 :(得分:0)

您的数据实际上并不是有效的XML,因为每个PARAM都没有结束标记。因此,您需要在运行XML Parser之前清理数据,或者使用正则表达式。

以下使用正则表达式来解析任意数量的字段和值:

use strict;
use warnings;

my %seen_header;
my @headers;
my @data = {};

while (<DATA>) {
    if (m{<PARAM NAME = "(.*?)"><VALUE>(.*?)</VALUE>}i) {
        my $name = $1;
        my $val = $2;

        push @headers, $name if ! $seen_header{$name}++;
        push @data, {} if exists $data[-1]{$name};
        $data[-1]{$name} = $val;

    } else {
        warn "Unrecognized format at line $.: $_"
    }
}

print "@headers\n";
print join(' ', map {$_ // ''} @{$_}{@headers}), "\n" for (@data);

__DATA__
<PARAM NAME = "A"><VALUE>1</VALUE>
<PARAM NAME = "B"><VALUE>3</VALUE>
<PARAM NAME = "C"><VALUE>43</VALUE>
<PARAM NAME = "A"><VALUE>6</VALUE>
<PARAM NAME = "B"><VALUE>3</VALUE>
<PARAM NAME = "C"><VALUE>13</VALUE>

输出:

A B C
1 3 43
6 3 13

还可以修改此代码以使用XML Parser,但如果它是你想要的,我会把它留给你。