XML解析仅适用于较大的xml文件的子集

时间:2014-05-24 21:38:48

标签: xml perl parsing xpath

use XML::LibXML;
use Data::Dumper; 

#parsing file
my $dom = XML::LibXML->new->parse_file('sample.xml');

#print file to make sure it looks ok
print $dom, "\n";

    #finds shortnames
    my $sn = $dom->findnodes('//shortName');
    print 'ShortName: '.$sn, "\n";

    #finds dbRefernce ids that are of type EC
    my $ids = $dom->findnodes('//dbReference[@type="EC"]/@id');
    my $number =()= $ids =~ /\./gi;
    print 'Result: '.$ids, "\n";

    #finds sequences that have a length
    my $seq = $dom->findnodes('//sequence[@length>1]');
    $seq =~ s/" "/"\n"/;
    print 'Sequence: '.$seq, "\n";

我有这个代码,它接受所有短名称,EC类型的dbReferences,以及具有长度的序列并打印它们。我有sample.xml(https://www.dropbox.com/s/dq8ir9f22cnfwrz/Sample.xml) 这是我需要解析的最大文件。但我一直在使用oneentry.xml(https://www.dropbox.com/s/6nxexfig46sw0v6/oneentry.xml),它只是较大列表中的一个条目。

问题是代码适用于一个条目并打印出来:

ShortName: 17-beta-HSD 53-alpha-HSD type 2DD-3DD3PGFS
Result: 1.-.-.-1.1.1.3571.1.1.1121.1.1.1881.1.1.2391.1.1.641.3.1.20
Sequence:  MDSKHQCVKLNDGHFMPVLGFGTYAPPEVPRSKALEVTKLAIEAGFRHIDSAHLYNNEEQ VGLAIRSKIADGSVKREDIFYTSKLWSTFHRPELVRPALENSLKKAQLDYVDLYLIHSPM SLKPGEELSPTDENGKVIFDIVDLCTTWEAMEKCKDAGLAKSIGVSNFNRRQLEMILNKP GLKYKPVCNQVECHPYFNRSKLLDFCKSKDIVLVAYSALGSQRDKRWVDPNSPVLLEDPV LCALAKKHKRTPALIALRYQLQRGVVVLAKSYNEQRIRQNVQVFEFQLTAEDMKAIDGLD RNLHYFNSDSFASHPNYPYSDEY 

但它不会为整个文件输出任何内容。与使脚本功能不相同的两个文件有什么不同?

1 个答案:

答案 0 :(得分:4)

这两个文件有一个重要区别。您的小文件oneentry.xml的开头如下:

<uniprot> 
    <entry dataset="Swiss-Prot" created="1995-11-01" modified="2014-05-14" version="156"> 
        <accession>P42330</accession> 
        <accession>A8K2V0</accession> 
        ...

但你的大号Sample.xml略有不同:

<uniprot xmlns="http://uniprot.org/uniprot" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://uniprot.org/uniprot 
         http://www.uniprot.org/support/docs/uniprot.xsd">
    <entry dataset="Swiss-Prot" created="1995-11-01" modified="2014-05-14" version="156"> 
        <accession>P42330</accession> 
        <accession>A8K2V0</accession> 
        ...

较大的文件声明默认命名空间:xmlns="http://uniprot.org/uniprot"而较小的命名空间不会。这将影响节点的选择。如果您的XML文件具有默认命名空间(其标签不需要前缀),则无关紧要。 XPath数据模型忽略了这一点。 XPath表达式假定元素选择器属于 no-namespace ,除非它们显式限定为前缀,该前缀映射到文件中声明的命名空间对于那些元素。

有两种方法可以解决这个问题:

  • 注册命名空间/前缀映射
  • 忽略表达式中的命名空间

  1. 注册命名空间

    这是推荐的解决方案,因为它可以保证您正在提取正确的节点。你需要选择一个前缀。前缀可以是任何合法的XML标识符uniprotuup - 您决定。您需要获取文档的XPath上下文:

    my $context = XML::LibXML::XPathContext->new( $dom->documentElement()  );
    $context->registerNs('u', 'http://uniprot.org/uniprot');
    

    现在所有你的XPath选择器必须加前缀。替换

    //shortName
    //dbReference[@type="EC"]/@id
    //sequence[@length>1]
    

    //u:shortName
    //u:dbReference[@type="EC"]/@id
    //u:sequence[@length>1]
    

    (假设您选择u作为前缀。)

    这不是这种情况,但如果你有一个包含多个步骤的路径,则需要在每个步骤中限定元素选择器。例如,如果你必须使用绝对表达式,你会写:

    /u:uniprot/u:entry/u:reference[16]/u:citation/u:dbReference[@type="EC"]/@id
    
  2. 忽略命名空间

    这是一种替代解决方案,可以有时被使用(通常用于小的明确选择,我认为不是你的情况)。您选择所有元素(使用任何元素通配符:*),然后使用标记名称的 local 部分进行过滤谓词(使用local-name()函数。对于此解决方案,您不必注册任何名称空间。您只需更改表达式。

    //*[local-name() = 'shortName']
    //*[local-name() = 'dbReference'][@type="EC"]/@id
    //*[local-name() = 'sequence'][@length>1]
    

    此解决方案的问题在于,如果您有两个具有相同本地名称且位于不同名称空间的元素,那么它们也将被选中。假设您没有任何冲突名称,此解决方案的优点是您可以在文件中使用它,具有名称空间的文件和没有名称空间的文件。