【问题标题】:XML Parsing works for only subset of larger xml fileXML 解析仅适用于较大 xml 文件的子集
【发布时间】:2014-05-24 21:38:47
【问题描述】:
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 

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

【问题讨论】:

  • 在我看来,这些数据只在上下文中有意义(在大文档中)。如果是这种情况,您将不得不考虑在选择节点时,因为//simpleName 将选择整个文档中的all 简单名称并将它们全部放在一起。我想您可能希望按entry 对它们进行分组,例如:entry[name='AK1C3_HUMAN']//shortName 将返回具有该孩子的entry 的所有短名称。
  • 这种选择在 XSLT 或 XQuery 中非常简单。
  • 查看这个fiddle 会生成您正在寻找的结果。当您从数据(左上框)+ XSLT 样式表(左侧第二个框)中按RUN 时,会生成右侧表格中的 HTML。这是提取数据的另一种方法。

标签: xml perl parsing xpath


【解决方案1】:

这两个文件有一个重要的区别。你的小文件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 文件是否具有 default 命名空间(不需要为其标记添加前缀)并不重要。 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. 忽略命名空间

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

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

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

【讨论】:

  • 不错的答案+1。我希望你对我的编辑感到满意。如果您更喜欢原版,请务必将其还原:我无法继续使用大声的 &lt;h2&gt; 标题
  • 您在第一个框中提供的代码是否在解析 $dom 后运行?然后我还在下面使用$dom来查找我想要的数据?
  • 是的。您运行 XPath 表达式之前注册命名空间。
  • 它应该可以工作。您也可以尝试使用$dom-&gt;documentElement-&gt;findnodes('//u:shortName'); 代替$dom-&gt;findnodes('//u:shortName');,或者在// 之前添加.。我相信这些是某些实现中错误的解决方法。
猜你喜欢
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
  • 2020-08-19
  • 1970-01-01
相关资源
最近更新 更多