8

おそらく非常に大きな JSON ファイルがあり、すべてをメモリにロードするのではなく、そこからストリーミングしたいと考えています。からの次の声明(強調を追加)に基づいて、JSON::XSそれは私のニーズに合わないと思います. ディスクから結果をストリーミングする Perl 5 JSON モジュールはありますか?

場合によっては、JSON テキストの増分解析が必要になることがあります。このモジュールは常に、JSON テキストと結果の Perl データ構造の両方を一度にメモリに保持する必要がありますが、JSON ストリームを段階的に解析できます。これは、完全な JSON オブジェクトが得られるまでテキストを蓄積し、それをデコードできるようにすることによって行われます。このプロセスは、decode_prefix を使用して完全な JSON オブジェクトが利用可能かどうかを確認するのと似ていますが、はるかに効率的です (また、最小限のメソッド呼び出しで実装できます)。

明確にするために、JSON にはオブジェクトの配列が含まれます。ファイルから一度に 1 つのオブジェクトを読み取りたい。

4

5 に答える 5

13

使いやすさと速度の点ではJSON::SL、勝者のようです。

#!/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"  }
]

JSON::Streaming::Readerは問題ありませんでしたが、速度が遅く、インターフェイスが冗長すぎるという問題があります (多くのコードリファレンスは何もしませんが、これらすべてのコードリファレンスが必要です)。

#!/usr/bin/perl

use strict;
use warnings;

use JSON::Streaming::Reader;

my $p = JSON::Streaming::Reader->for_stream(\*DATA);

my $obj;
my $attr;
$p->process_tokens(
    start_array    => sub {}, #who cares?
    end_array      => sub {}, #who cares?
    end_property   => sub {}, #who cares?
    start_object   => sub { $obj = {}; },     #clear the current object
    start_property => sub { $attr = shift; }, #get the name of the attribute
    #add the value of the attribute to the object
    add_string     => sub { $obj->{$attr} = shift; },
    add_number     => sub { $obj->{$attr} = shift; },
    #object has finished parsing, it can be used now
    end_object     => sub { print "$obj->{n}: $obj->{s}\n"; },
);

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

1,000 レコードを解析するのにJSON::SL0.2 秒とJSON::Streaming::Reader3.6 秒かかりJSON::SLました (一度に 4k が供給されていたため、JSON::Streaming::Reader のバッファー サイズを制御できませんでした)。

于 2012-09-17T15:08:42.977 に答える
3

search.cpan.org で「JSON ストリーム」を検索しているときに最初に表示されるJSON::Streaming::Readerを見たことがありますか?

あるいは、「JSON SAX」を検索して見つけたJSON::SL - 明白な検索用語ほどではありませんが、XML の SAX パーサーのように聞こえます。

于 2012-09-17T13:49:14.850 に答える
2

これは、完全な JSON オブジェクトが得られるまでテキストを蓄積し、それをデコードできるようにすることによって行われます。

これがあなたを台無しにするものです。JSON ドキュメント1 つのオブジェクトです。

インクリメンタル解析から何を望むかをより明確に定義する必要があります。大規模なマッピングの 1 つの要素を探していますか? 読み書きした情報で何をしようとしていますか?


配列から一度に 1 つの要素を読み取ることによって、JSON データを段階的に解析するライブラリを知りません。ただし、これは有限状態オートマトンを使用して自分で実装するのは非常に簡単です (基本的に、ファイルには、\s*\[\s*([^,]+,)*([^,]+)?\s*\]\s*文字列内のコンマを正しく解析する必要があることを除いて、この形式があります)。

于 2012-09-17T13:22:19.467 に答える
2

[最初の右ブラケット、次にコンマをスキップしようとしましたか,:

$json->incr_text =~ s/^ \s* \[ //x;
...
$json->incr_text =~ s/^ \s* , //x;
...
$json->incr_text =~ s/^ \s* \] //x;

3 番目の例のように: http://search.cpan.org/dist/JSON-XS/XS.pm#EXAMPLES

于 2012-09-17T13:43:04.797 に答える
1

JSON の生成方法を制御できる場合は、プリティ フォーマットをオフにして、1 行に 1 つのオブジェクトを出力することをお勧めします。これにより、次のように解析が簡単になります。

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;
}
于 2013-06-14T10:29:26.747 に答える