2015-03-24 77 views
1

我有一個解析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,而沒有重複的消息。

+1

我可能會寫你的過濾器的文件轉換成執行過濾XSLT文件XSLT文件。 – reinierpost 2015-03-25 11:16:24

+0

@reinierpost是的,我有點希望我走了那條路,但已經沉浸在LibXML中,並不想開始學習別的東西。似乎最好堅持我習慣的方式。 – 2015-03-25 14:38:05

+0

@reinierpost也,是不是增加了一個額外的步驟?將filter.xml轉換爲XSLT,然後執行過濾器?我認爲我提出的解決方案可能不適用於大型log.xml文件(尚未嘗試過),但是我認爲當節點被標記爲保留時,我可以使用更多的標誌來加速它以打破循環當然。 – 2015-03-25 14:41:30

回答

1

我想我很可能將這個問題標題很差,但是希望這個答案和答案有一天能夠幫助別人。無論如何,我已經用一些蠻力完成了我的目標......我最終爲每個過濾器做了一個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); 
    }