如何从文件中传输JSON?

如何从文件中传输JSON?,json,perl,stream,Json,Perl,Stream,我将有一个可能非常大的JSON文件,我希望从中流式传输,而不是将其全部加载到内存中。根据以下声明(我添加了重点),我认为它不适合我的需要。是否有一个Perl5JSON模块将从磁盘流式传输结果 在某些情况下,需要对JSON文本进行增量解析。虽然此模块总是必须同时将JSON文本和生成的Perl数据结构保存在内存中,但它确实允许您增量解析JSON流。它通过累积文本直到拥有一个完整的JSON对象,然后对其进行解码来实现这一点。这个过程类似于使用decode_前缀来查看是否有完整的JSON对象可用,但效率

我将有一个可能非常大的JSON文件,我希望从中流式传输,而不是将其全部加载到内存中。根据以下声明(我添加了重点),我认为它不适合我的需要。是否有一个Perl5JSON模块将从磁盘流式传输结果

在某些情况下,需要对JSON文本进行增量解析。虽然此模块总是必须同时将JSON文本和生成的Perl数据结构保存在内存中,但它确实允许您增量解析JSON流。它通过累积文本直到拥有一个完整的JSON对象,然后对其进行解码来实现这一点。这个过程类似于使用decode_前缀来查看是否有完整的JSON对象可用,但效率要高得多(并且可以通过最少的方法调用来实现)

为了澄清这一点,JSON将包含一个对象数组。我想从文件中一次读取一个对象

它通过累积文本直到拥有一个完整的JSON对象,然后对其进行解码来实现这一点

这就是你被搞砸的地方。JSON文档是一个对象

您需要更清楚地定义您想要从增量解析中得到什么。您正在寻找大型映射的一个元素吗?你想对你读/写的信息做什么



我不知道有哪个库会通过一次从数组中读取一个元素来增量解析JSON数据。但是,使用有限状态自动机实现这一点非常简单(基本上,您的文件的格式是
\s*\[\s*([^,]+,)*([^,]+)?\s*\]\s*
,除非您需要正确解析字符串中的逗号。)

您是否尝试先跳过右括号
[
,然后跳过逗号

$json->incr_text =~ s/^ \s* \[ //x;
...
$json->incr_text =~ s/^ \s* , //x;
...
$json->incr_text =~ s/^ \s* \] //x;
就像第三个例子:

在search.cpan.org上搜索“JSON流”时,您是否查看了第一个显示的内容


或者通过搜索“JSON SAX”来找到—虽然不是很明显的搜索词,但您描述的内容听起来像是XML的SAX解析器。

就易用性和速度而言,似乎是赢家:

#!/usr/bin/perl

use strict;
use warnings;

use JSON::SL;

my $p = JSON::SL->new;

#look for everthing past the first level (i.e. everything in the array)
$p->set_jsonpointer(["/^"]);

local $/ = \5; #read only 5 bytes at a time
while (my $buf = <DATA>) {
    $p->feed($buf); #parse what you can
    #fetch anything that completed the parse and matches the JSON Pointer
    while (my $obj = $p->fetch) {
        print "$obj->{Value}{n}: $obj->{Value}{s}\n";
    }
}

__DATA__
[
    { "n": 0, "s": "zero" },
    { "n": 1, "s": "one"  },
    { "n": 2, "s": "two"  }
]

解析1000条记录花了
JSON::SL
0.2秒和
JSON::Streaming::Reader
3.6秒(注意,
JSON::SL
一次输入4k,我无法控制JSON::Streaming::Reader的缓冲区大小).

如果您可以控制如何生成JSON,那么我建议您关闭格式设置,每行打印一个对象。这使得解析变得简单,如下所示:

use Data::Dumper;
use JSON::Parse 'json_to_perl';
use JSON;
use JSON::SL;
my $json_sl = JSON::SL->new();
use JSON::XS;
my $json_xs = JSON::XS->new();
$json_xs = $json_xs->pretty(0);
#$json_xs = $json_xs->utf8(1);
#$json_xs = $json_xs->ascii(0);
#$json_xs = $json_xs->allow_unknown(1);

my ($file) = @ARGV;
unless( defined $file && -f $file )
{
  print STDERR "usage: $0 FILE\n";
  exit 1;
}


my @cmd = ( qw( CMD ARGS ), $file );
open my $JSON, '-|', @cmd or die "Failed to exec @cmd: $!";

# local $/ = \4096; #read 4k at a time
while( my $line = <$JSON> )
{
  if( my $obj = json($line) )
  {
     print Dumper($obj);
  }
  else
  {
     die "error: failed to parse line - $line";
  }
  exit if( $. == 5 );
}

exit 0;

sub json
{
  my ($data) = @_;

  return decode_json($data);
}

sub json_parse
{
  my ($data) = @_;

  return json_to_perl($data);
}

sub json_xs
{
  my ($data) = @_;

  return $json_xs->decode($data);
}

sub json_xs_incremental
{
  my ($data) = @_;
  my $result = [];

  $json_xs->incr_parse($data);  # void context, so no parsing
  push( @$result, $_ ) for( $json_xs->incr_parse );

  return $result;
}

sub json_sl_incremental
{
  my ($data) = @_;
  my $result = [];

  $json_sl->feed($data);
  push( @$result, $_ ) for( $json_sl->fetch );
  # ? error: JSON::SL - Got error CANT_INSERT at position 552 at json_to_perl.pl line 82, <$JSON> line 2.

  return $result;
}
使用数据::转储程序;
使用JSON::解析“JSON_to_perl”;
使用JSON;
使用JSON::SL;
我的$json_sl=json::sl->new();
使用JSON::XS;
我的$json_xs=json::xs->new();
$json_xs=$json_xs->pretty(0);
#$json_xs=$json_xs->utf8(1);
#$json_xs=$json_xs->ascii(0);
#$json_xs=$json_xs->allow_unknown(1);
我的($file)=@ARGV;
除非(定义为$file&&f$file)
{
打印STDERR“用法:$0文件\n”;
出口1;
}
my@cmd=(qw(cmd参数),$file);
打开我的$JSON、'-|'、@cmd或die“无法执行@cmd:$!”;
#本地$/=\4096;#一次读取4k
while(我的$line=)
{
if(my$obj=json($line))
{
打印转储程序($obj);
}
其他的
{
die“错误:无法分析行-$line”;
}
如果($==5)退出;
}
出口0;
子json
{
我的($数据)=@;
返回decode_json($data);
}
子json_解析
{
我的($数据)=@;
将json_返回到_perl($data);
}
子json_xs
{
我的($数据)=@;
返回$json_xs->decode($data);
}
子json_xs_增量
{
我的($数据)=@;
我的$result=[];
$json_xs->incr_parse($data);#无效上下文,因此没有解析
推送(@$result,$)($json_xs->incr_parse);
返回$result;
}
子json_sl_增量
{
我的($数据)=@;
我的$result=[];
$json_sl->feed($data);
为($json_sl->fetch)推送(@$result,$);
#?错误:JSON::SL-在JSON_至_perl.pl第82行第2行的位置552处出现错误无法插入。
返回$result;
}

我更新了这个问题,JSON是一个对象数组,我一次只想要一个对象。我想你……你是在找人来证明某个模块吗?我的初步搜索显示,JSON是一个潜在的候选对象,但不知道它们是否适合你的需要。@Zaid我只是在看JSON模块,我不认为需要lude stream的名字(我错误地认为顶级狗会有这个功能)。我还没有尝试过任何东西,文档似乎说它仍然在内存中保存东西(即没有像
XML::Twig
has那样的刷新).事实上,第四个例子似乎做了你想做的事情。看起来是这样,但它看起来像地狱一样粗糙。显然CPAN上还有其他专门的模块可以对我隐藏这些粗糙之处。我现在正在查看它们。我现在正在将文档读到JSON::SL,接下来我将查看JSON::Streaming::Reader。作为JSON::SL的作者,它提供了rs既有一个拉式接口(如下面的示例所示),也有一个实际的SAX式接口(称为
Tuba
)。它们是不同的,SAX式接口的缺点是显而易见的(复杂且缓慢)
use Data::Dumper;
use JSON::Parse 'json_to_perl';
use JSON;
use JSON::SL;
my $json_sl = JSON::SL->new();
use JSON::XS;
my $json_xs = JSON::XS->new();
$json_xs = $json_xs->pretty(0);
#$json_xs = $json_xs->utf8(1);
#$json_xs = $json_xs->ascii(0);
#$json_xs = $json_xs->allow_unknown(1);

my ($file) = @ARGV;
unless( defined $file && -f $file )
{
  print STDERR "usage: $0 FILE\n";
  exit 1;
}


my @cmd = ( qw( CMD ARGS ), $file );
open my $JSON, '-|', @cmd or die "Failed to exec @cmd: $!";

# local $/ = \4096; #read 4k at a time
while( my $line = <$JSON> )
{
  if( my $obj = json($line) )
  {
     print Dumper($obj);
  }
  else
  {
     die "error: failed to parse line - $line";
  }
  exit if( $. == 5 );
}

exit 0;

sub json
{
  my ($data) = @_;

  return decode_json($data);
}

sub json_parse
{
  my ($data) = @_;

  return json_to_perl($data);
}

sub json_xs
{
  my ($data) = @_;

  return $json_xs->decode($data);
}

sub json_xs_incremental
{
  my ($data) = @_;
  my $result = [];

  $json_xs->incr_parse($data);  # void context, so no parsing
  push( @$result, $_ ) for( $json_xs->incr_parse );

  return $result;
}

sub json_sl_incremental
{
  my ($data) = @_;
  my $result = [];

  $json_sl->feed($data);
  push( @$result, $_ ) for( $json_sl->fetch );
  # ? error: JSON::SL - Got error CANT_INSERT at position 552 at json_to_perl.pl line 82, <$JSON> line 2.

  return $result;
}