2012-08-06 29 views
2

我有一個項目需要解析複雜的XML數據。我決定和XML::Twig一起去,大部分都能很好地工作。我遇到了一個問題,其中不同的信息具有相同的標籤名稱,但處於不同的路徑。像下面的那樣,DateOfBirth用於兩個不同的領域。XML :: Twig - 管理具有相同標記的字段

<doc:DForm xmlns:doc="urn:xml-gov-au:..."> 
    <doc:PersonsDetails> 
     <doc:GivenName LanguageIdentifier="" LanguageLocaleIdentifier=""> 
      John 
     </doc:GivenName> 
     <doc:Surname LanguageIdentifier="" LanguageLocaleIdentifier=""> 
      Citizen 
     </doc:Surname> 
     <doc:DateOfBirth LanguageIdentifier="" LanguageLocaleIdentifier=""> 
      2012-06-14 
     </doc:DateOfBirth> 
    </doc:PersonsDetails> 
    <doc:SupportingInformation> 
     <doc:NumberOfSiblings> 
     5.00 
     </doc:NumberOfSiblings> 
     <doc:SiblingsDetails> 
     <doc:DateOfBirth LanguageIdentifier="" LanguageLocaleIdentifier=""> 
     2009-03-18 
     </doc:DateOfBirth> 
     <doc:Name LanguageIdentifier="" LanguageLocaleIdentifier=""> 
     James Citizen</doc:Name> 
     </doc:SiblingsDetails> 
     <doc:SiblingsDetails> 
     <doc:DateOfBirth LanguageIdentifier="" LanguageLocaleIdentifier=""> 
      2006-08-17 
     </doc:DateOfBirth> 
     <doc:Name LanguageIdentifier="" LanguageLocaleIdentifier=""> 
      Jane Citizen 
     </doc:Name> 
     </doc:SiblingsDetails> 
     <doc:Address> 
      <doc:Street>25 test street<doc:Street> 
      <doc:City>Melbourne <doc:City> 
      <doc:PostalCode>3000<doc:PostalCode> 
     <doc:Address> 
    </doc:SupportingInformation> 
    </doc:MCCPDForm> 

我有安裝多個處理程序來處理不同的信息,但我們並不需要的兄弟姐妹的細節,它被以基於這些字段映射到XML元素一個2級哈希端部處理。

樣品:

my %field = ( 
     "DetDateOfBirth" => { 
    "type" => "Date", 
    "value" => undef, 
    "dbfield" => "DetDateOfBirth", 
    }, 
) 

所以,當兄弟姐妹的出生日期正在處理,它將使用上述哈希元素來進行設置,但是當人的出生日期進行處理,因爲已經有一個值,它會轉移到下一個元素。

所以我建立了另一個處理程序,並確保信息之前處理。

現在,問題是,想象有多種情況下,同一名稱用於多個元素,但使用不同的路徑。我只是寫更多的處理程序,還是有另一種更好的管理這種情況的方式。

的代碼,培訓相關

my $namespace = "doc"; 
my $formname = "DForm"; 
enter code here 
my $twig = XML::Twig->new(
    pretty_print => 'indented', 
    twig_handlers => { 
     "$namespace:${formname}/$namespace:PersonsDetails/$namespace:Address" => 
      \&ProcessAddress, 
     "$namespace:${formname}/$namespace:SupportingInformation" => 
      \&ProcessSupportingInformation, 
     "bie1:PdfFile"   => \&DecodePDF, 
     "$namespace:${formname}" => \&ProcessRecord, 
    } 
); 


sub ProcessRecord { 
    my $twg = shift; 
    my $record = shift; 
    my $fld; 
    my $value; 
    my $irn; 

    my $elt = $record; 

    while ($elt = $elt->next_elt($record)) { 
     $fld = $elt->tag(); 

     $fld =~ s/^$namespace\://; 


     if (defined $fields{$fld}{"type"} && $elt->text) { 
      if ($fld =~ /NameOfPlaceInstitution|HospitalNameOfBirth/i) { 
       next if $elt->text =~ /Other location/i; 
      } 

      if (!defined $fields{$fld}{"value"}) { 
       $fields{$fld}{"value"} = $elt->text; 
      } 

     } 
    } 
} 

sub ProcessSupportingInformation { 
    my $twg = shift; 
    my $record = shift; 
    my $fld; 
    my $value; 
    my $parent; 

    my $elt = $record; 

    while ($elt = $elt->next_elt($record)) { 
     $fld = $elt->tag(); 
     $fld =~ s/^$namespace\://; 

     $parent = $elt->parent(); 

     next if ($fld =~ /PCDATA/); 

     if (defined $fields{$fld}{"type"} && $elt->text) { 
      if ($fld =~ /PlaceOfDeathHospital/i) { 
       if ($elt->text =~ /Other location/i) { 
        next; 
       } 
      } 

        if ($fld =~ /StreetAddress/i) { 
       $fields{"StreetAddressOfPerson"} = $elt->text; 
      } 
      else { 
       if (!defined $fields{$fld}{"value"}) { 
        $fields{$fld}{"value"} = $elt->text; 
       } 
      } 
     } 
     else { 
      $record->delete; 
     } 
    } 

} 

只是一個供參考,實際的XML文件是大約700行,其包括編碼PDF爲好。

另一種選擇是在散列中設置另一個標記,將標記映射到數據庫字段並在第一次處理信息時進行設置。

謝謝

PS:抱歉太多的編輯。我想我現在就知道了。

PPS:有代碼中的一個敏感的信息,以及XML,我無法展現,所以我不得不修改它的部分......

回答

2

由於您已將問題縮減到XML無效的地步(它始於<doc:DForm>,但以<doc:MCCPDForm>結尾)並且Perl代碼與XML數據不對應,因此很難理解您的確切情況。我想你錯誤地使用了XML::Twig。 「樹枝」主要是爲了將XML文件縮減爲可以獨立處理的一系列記錄,而不是作爲訪問數據內部各個元素的基礎。

你不說<bie1:PdfFile>元素如何與<PersonsDetails>,所以我不能對這些評論,但它看起來像有是包含<PersonsDetails>和相關<SupportingInformation>沒有一個單一的元素,這樣他們就可以被捆綁一起只在他們的鄰接文件中。

如果是這種情況,那麼我只會把一個處理程序放在這兩個元素上,代碼看起來像下面的程序。

在特定的上下文中遇到所有<DateOfBirth>元素的含義很容易區分 - 在ProcessPersonDetails或之內,作爲兄弟姐妹列表之一。

該程序只是打印您的示例XML中可用的信息。建立數據庫記錄並不難,而是在處理給定人員的最後數據的末尾寫入數據庫記錄。

還請注意purge的調用,這是從存儲器中刪除處理後的信息所必需的。如果沒有這個,沒有一次處理數據的樹枝處理,而不是與整個文檔的好處

use strict; 
use warnings; 

use XML::Twig; 

my $twig = XML::Twig->new(
    twig_handlers => { 
     'doc:PersonsDetails' => \&ProcessPersonsDetails, 
     'doc:SupportingInformation' => \&ProcessSupportingInformation 
    } 
); 

$twig->parsefile('DForm.xml'); 


sub ProcessPersonsDetails { 
    my ($twig, $record) = @_; 
    print "PersonsDetails\n"; 
    for (qw/ doc:GivenName doc:Surname doc:DateOfBirth /) { 
     print ' ', $record->first_child_trimmed_text($_), "\n"; 
    } 
} 

sub ProcessSupportingInformation { 
    my ($twig, $record) = @_; 
    print "SupportingInformation\n"; 
    for my $sibling ($record->children('doc:SiblingsDetails')) { 
     print " Sibling\n"; 
     for (qw/ doc:DateOfBirth doc:Name /) { 
      print ' ', $sibling->first_child_trimmed_text($_), "\n"; 
     } 
    } 
    $twig->purge; 
} 

輸出

PersonsDetails 
    John 
    Citizen 
    2012-06-14 
SupportingInformation 
    Sibling 
    2009-03-18 
    James Citizen 
    Sibling 
    2006-08-17 
    Jane Citizen 

更新

如果有是每個文件只有一個記錄,那麼XML::Twig處理XML數據的能力不需要創建文件,整個文檔可以立即加載並處理。

這個程序確實如此,併產生與前面的代碼相同的輸出。不必編寫在解析過程中調用的處理程序,代碼更加簡潔

use strict; 
use warnings; 

use XML::Twig; 

my $twig = XML::Twig->new(discard_all_spaces => 1); 
my $root = $twig->parsefile('DForm.xml')->root; 

print "PersonsDetails\n"; 
my $details = $root->first_child('doc:PersonsDetails'); 
for (qw/ GivenName Surname DateOfBirth /) { 
    my $value = $details->trimmed_field("doc:$_"); 
    print " $value\n"; 
} 

print "SupportingInformation\n"; 
my @siblings = $root->first_child('doc:SupportingInformation')->children; 
for my $sib (@siblings) { 
    print " Sibling\n"; 
    for (qw/ Name DateOfBirth /) { 
    my $value = $sib->trimmed_field("doc:$_"); 
    print " $value\n"; 
    } 
} 
+0

謝謝!太棒了。表單名稱只是我的錯誤。它應該是'DForm',是的,忽略DecodePDF位。我試圖省略不相關的信息,並在這個過程中填入了一些細節。對於那個很抱歉。儘管如此,你給了我一個很好的起點,我相信我可以從這裏拿走它。謝謝:) – Hameed 2012-08-06 13:29:26

+0

我很高興它有幫助。你的XML是什麼樣的?每個文件都是單個人,還是文件中有多個「」元素?這些人的細節和支持信息以某種方式結合在一起? – Borodin 2012-08-06 13:32:59

+0

每個文件都是一個記錄,並且是一個文件中的所有內容都捆綁在一起。但是,我確實有另一個項目,它將在一個巨大的XML文件中有多個記錄。類似的信息,不同的來源。 – Hameed 2012-08-06 13:49:02

1

這是一個有點難以回答你的問題沒有看到任何代碼,但你有沒有看過在更長的路徑上觸發處理程序,例如doc:PersonsDetails/doc:DateOfBirth? 這將確保只在正確的上下文中處理日期。

+0

我已經添加了一些代碼,並重新編寫了一些部分。基本上,我想知道是否有一個更好的方式來寫幾個處理程序。回答你的問題,是的。這就是我現在通過添加處理程序來修復它的方法。 – Hameed 2012-08-06 06:13:12

+0

恐怕我不太理解你的代碼,這可能是因爲它還早,我需要更多的咖啡; - (現在我甚至不知道你的問題是什麼!如果你不感興趣的內容一些元素,比如'doc:SupportingInformation',會使用'ignore_elts'選項幫助嗎?如果使用'ignore_elts => {'doc:SupportingInformation'=>'discard'}',那麼它會跳過整個元素,而不會被包含在樹中,因此其中的處理程序不會被觸發,還有其他方法可以使用'ignore_elts'來存儲元素的內容或者將其原樣輸出 – mirod 2012-08-06 06:36:51

+0

這是我的代碼,它是不完整的。我不明白,如果有人向我展示不完整的代碼,也許我會錯誤地使用XML :: Twig模塊,但是謝謝你嘗試:) – Hameed 2012-08-06 06:51:53

相關問題