多次解析XML文件并将结果合并在一起
问题描述:
我有一个解析2个XML的子例程,一个是原始日志数据,另一个是过滤器。我想从log.xml中删除其中一个筛选器中找不到的所有内容。多次解析XML文件并将结果合并在一起
这是我的日志文件的例子:
<log>
<message>
<type>warning</type>
<from>cody</from>
<content>cant use XML::Merge</content>
</message>
<message>
<type>error</type>
<from>cody</from>
<content>some text here</content>
</message>
<message>
<type>warning</type>
<from>charlie</from>
<content>ruff</content>
</message>
<message>
<type>error</type>
<from>cody</from>
<content>an error</content>
</message>
</log>
与filter.xml看起来像:
<filters>
<filter>
<type>warning</type>
<content>XML::Merge</content>
</filter>
<filter>
<type>error</type>
</filter>
</filters>
这将导致包含内容的所有警告“XML ::合并“被保留和所有错误。我试图用第一个过滤器进行第一次传递,这导致所有其他消息节点被切断,所以我在得到的XML文件中没有错误。接下来的过滤器会剔除第一个过滤器应该保留的过滤器。这里是我的代码,如果在filter.xml中只有一个过滤器,那么运行良好。
sub include {
my $filterParser = XML::LibXML->new->parse_file($filterXML);
my $logParser = XML::LibXML->new->parse_file($xml);
foreach my $filter ($filterParser->findnodes('/filters/filter')) {
foreach my $msg ($logParser->findnodes('/log/message')) {
foreach my $msgNode ($msg->childNodes) {
foreach my $filterNode ($filter->childNodes) {
if ($msgNode->localName eq $filterNode->localName) {
my $m = $msgNode->textContent;
my $f = $filterNode->textContent;
if (index($m, $f) == -1) {
$msg->parentNode->removeChild($msg);
}
}
}
}
}
}
$logParser->toFile($xml);
}
我明白为什么它输出与多个过滤器一个空白文档,但需要帮助上获得第一遍的地方保存,然后用原始的XML做出通过用第二过滤器,等等,直到没有剩余的过滤器,然后将所有内容合并成一个XML,而没有重复的消息。
答
我想我很可能将这个问题标题很差,但是希望这个答案和答案有一天能够帮助别人。无论如何,我已经用一些蛮力完成了我的目标......我最终为每个过滤器做了一个pass,并且将我想要保留的节点添加到列表中(我需要一个标志,因为某些过滤器有多个单一标准)。在所有消息的所有过滤器都被处理后,然后循环访问log.xml并查找保存在我的列表中的每个节点。如果log.xml中的节点与列表中的任何节点不匹配,我将它从树中删除。
sub include {
my $filterParser = XML::LibXML->new->parse_file($filterXML);
my $logParser = XML::LibXML->new->parse_file($xml);
my $remove = true;
my @nodes;
foreach my $msg ($logParser->findnodes('/TdsMainLog/message')) {
foreach my $filter ($filterParser->findnodes('/filters/filter')) {
foreach my $msgNode ($msg->childNodes) {
foreach my $filterNode ($filter->childNodes) {
if ($msgNode->localName eq $filterNode->localName) {
my $m = $msgNode->textContent;
my $f = $filterNode->textContent;
if (index($m, $f) != -1) {
#mark for keeping
$remove = false;
}
else { $remove = true; } #else unmark
}
}
}
if ($remove eq false) { push (@nodes, $msg); }
$remove = true;
}
}
foreach my $msg ($logParser->findnodes('/TdsMainLog/message')) {
$remove = true;
foreach my $node (@nodes) {
if ($msg->isSameNode($node)) {
$remove = false;
}
}
if ($remove eq true) { $msg->parentNode->removeChild($msg); }
}
$logParser->toFile($xml);
}
我可能会写你的过滤器的文件转换成执行过滤XSLT文件XSLT文件。 – reinierpost 2015-03-25 11:16:24
@reinierpost是的,我有点希望我走了那条路,但已经沉浸在LibXML中,并不想开始学习别的东西。似乎最好坚持我习惯的方式。 – 2015-03-25 14:38:05
@reinierpost也,是不是增加了一个额外的步骤?将filter.xml转换为XSLT,然后执行过滤器?我认为我提出的解决方案可能不适用于大型log.xml文件(尚未尝试过),但是我认为当节点被标记为保留时,我可以使用更多的标志来加速它以打破循环当然。 – 2015-03-25 14:41:30