XML :: Simple为大型XML返回“Out of memory”错误

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XML :: Simple为大型XML返回“Out of memory”错误相关的知识,希望对你有一定的参考价值。

这可能需要一段时间来解释,但我有一个文件(XMLList.txt),其中包含多个IDOC XML的路径。 XMLList.txt的内容如下所示:

/usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/AU_DHL_PW_Inbound_Delivery_from_Pfizer_20171220071754.xml /usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/AU_DHL_PW_Inbound_Delivery_from_Pfizer_20171220083310.xml /usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/CCMastOut_MQ_GLB_1_20171220154826.xml

我正在尝试创建一个Perl脚本,该脚本读取每个XML并将每个XML文件中的标签DOCNUM,SNDPRN和RCVPRN的值解析为管道分隔文件“report.csv”

需要注意的另一件事是我的XML文件可能是:所有在一行 - 例如

 <?xml version="1.0" encoding="UTF-8"?><ZDELVRY073PL><IDOC BEGIN="1">
    <EDI_DC40 SEGMENT="1"><TABNAM>EDI_DC40</TABNAM><MANDT>400</MANDT>
    <DOCNUM>0000000443474886</DOCNUM><DOCREL>731</DOCREL><STATUS>30</STATUS>
    <DIRECT>1</DIRECT><OUTMOD>4</OUTMOD><IDOCTYP>DELVRY07</IDOCTYP>
    <CIMTYP>ZDELVRY073PL</CIMTYP><MESTYP>ZIBDADV</MESTYP><MESCOD>IBG</MESCOD>
    <SNDPOR>SAPQ01</SNDPOR><SNDPRT>LS</SNDPRT><SNDPRN>Q01CLNT400</SNDPRN>
    <RCVPOR>XMLDIST_MT</RCVPOR><RCVPRT>LS</RCVPRT><RCVPFC>LS</RCVPFC>
    <RCVPRN>AU_DHL</RCVPRN>.... </EDI_DC40></IDOC>

或多行XML:

  <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
    <INVOIC02>
      <IDOC>
        <EDI_DC40>
      <TABNAM/>
      <DOCNUM>0000000658056255</DOCNUM>
      <DIRECT/>
      <IDOCTYP>INVOIC02</IDOCTYP>
      <MESTYP>INVOIC</MESTYP>
      <SNDPOR>SAPP01</SNDPOR>
      <SNDPRT/>
      <SNDPRN>ALE400</SNDPRN>
      <RCVPOR>XMLINVOICE</RCVPOR>
      <RCVPRT>KU</RCVPRT>
      <RCVPRN>C18BASWARE</RCVPRN>
      <CREDAT>20171220</CREDAT>
      <CRETIM>222323</CRETIM>
    </EDI_DC40>

到目前为止我使用的脚本似乎适用于小型XML。但是,一些> 50 MB的XML会抛出此错误:

内存不足!内存不足!回调在/usr/opt/perl5/lib/site_perl/5.10.1/XML/SAX/Base.pm第1941行调用退出(#1)(F)通过call_sv()从外部包调用的子程序通过调用exit退出。

内存不足!

所以,这是我正在使用的代码。希望你的帮助调整一下:

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
# use module
use XML::Simple;
use Data::Dumper;

# create object
my $xml = new XML::Simple; 

my $file_list = 'XMLList.txt';
open(my $fh_i, '<:encoding(UTF-8)', $file_list)
  or die "Could not open file '$file_list' $!";

my $csv_out = 'report.csv';
open(my $fh_o, '>', $csv_out)
  or die "Could not open file '$csv_out' $!"; 

while (my $row = <$fh_i>) {
  $row =~ s/R//g;
  my $data = $xml->XMLin($row);
  print $fh_o "$data->{IDOC}->{EDI_DC40}->{DOCNUM}|";
  print $fh_o "$data->{IDOC}->{EDI_DC40}->{SNDPRN}|";
  print $fh_o "$data->{IDOC}->{EDI_DC40}->{RCVPRN}
";
}

close $fh_o;
答案

我建议人们在使用时遇到问题时停止使用XML::Simple。该模块很适合入门,但它不是一个长期的解决方案。即便如此,请参阅Why is XML::Simple “Discouraged”?

XML::Twig是我经常用于完成这些任务的东西。您可以为标记设置处理程序并获取树的该部分。你处理它并继续前进。这可能就像这样简单,我设置了一个子程序来处理每个EDI_DC40,因为我遇到它:

use Text::CSV_XS;
use XML::Twig;

my $csv = Text::CSV_XS->new;

my $twig = XML::Twig->new(   
    twig_handlers => { 
        'EDI_DC40' => &process_EDI_DC40,
        },
    );

$twig->parsefile( $ARGV[0] );

sub process_EDI_DC40 {
    my( $twig, $thingy ) = @_;

    my @values = map { $thingy->first_child( $_ )->text } 
        qw(DOCNUM RCVPRN SNDPRN);

    $csv->say( *STDOUT, @values );
    }
另一答案

首先,如果文件包含换行符,

  while (my $row = <$fh_i>){
  $row =~ s/R//g;
  my $data = $xml->XMLin($row);

将从文件中一次读取一行,并尝试单独在该行上进行XML转换,而不是整个文档。我建议您将每个文件放入缓冲区并使用正则表达式在XMLin转换之前消除换行符和回车符。此外,如果文件中存在任何XML错误,XMLin将毫不客气地死亡,因此您希望在eval块中运行它。

以上是关于XML :: Simple为大型XML返回“Out of memory”错误的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PHP 将 Simple XML 解释为 HTML?

关于Ajax处理 返回的XML

找不到 XML/Simple.pm

如何使用 XML::Simple 根据 Perl 中的 XML 属性内容修改 XML 元素?

为啥 XML::Simple 不受欢迎?

动态访问嵌套对象