2012-08-06 55 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

相关问题