Text-JSON-Nibble
view release on metacpan or search on metacpan
lib/Text/JSON/Nibble.pm view on Meta::CPAN
package Text::JSON::Nibble;
=encoding utf8
=cut
use 5.006;
use strict;
use warnings;
use Data::Dumper;
=head1 NAME
Text::JSON::Nibble - Nibble complete JSON objects from buffers
=head1 VERSION
Version 1.01
=cut
our $VERSION = '1.01';
=head1 WARNING
This module should be used with caution, it will not handle 'badly formed' json well, its entire purpose was because I was experiencing
segfaults with Cpanel::XS's decode_prefix when dealing with a streaming socket buffer.
=head1 DESCRIPTION
This module is a 'character' crawling JSON extractor for plain TEXT, usable in both a 'streaming' or 'block' method, for when you need something that is not XS.
It is particularly handy for when you want to deal with JSON without decoding it.
=head1 SYNOPSIS
use Text::JSON::Nibble;
my $json = '{"lol":{"a":[1,2,3],"b":"lol"}}';
my $item = Text::JSON::Nibble->new();
my @results = @{ $item->digest($json) };
=head1 EXAMPLES
=head2 Example1 (Basic usage)
use Text::JSON::Nibble;
my $json = '{"lol":{"a":[1,2,3],"b":"lol"}}{"lol":{"a":[1,2,3],"b":"lol"}}';
my $item = Text::JSON::Nibble->new();
foreach my $jsonBlock ( @{ $item->digest($json) } ) {
print "Found: $jsonBlock\n";
}
# Will display the following:
# Found: {"lol":{"a":[1,2,3],"b":"lol"}}
# Found: {"lol":{"a":[1,2,3],"b":"lol"}}
=head2 Example2 (Basic usage - mangled JSON)
use Text::JSON::Nibble;
my $json = '\cxa4GL<A{"lol":{"a":[1,2,3],"b":"lol"}}He Random Stuf${"lol":{"a":[1,2,3],"b":"lol"}}\cxa4GL<A';
my $item = Text::JSON::Nibble->new();
foreach my $jsonBlock ( @{ $item->digest($json) } ) {
print "Found: $jsonBlock\n";
}
# Will display the following:
# Found: {"lol":{"a":[1,2,3],"b":"lol"}}
# Found: {"lol":{"a":[1,2,3],"b":"lol"}}
=head2 Example3 (Streaming usage for POE and others)
use Text::JSON::Nibble;
my @jsonStream = qw( {"test":1} {"moreTest":2} {"part ial":3} );
my $item = Text::JSON::Nibble->new();
$item->process( shift @jsonStream );
while( $item->stack ) {
my $jsonBlock = $item->pull;
print "Found $jsonBlock\n";
while ( my $newJSON = shift @jsonStream ) {
$item->process($newJSON);
}
}
=head1 Generic callers
=head2 new
Generate a new JSON Nibble object
=cut
sub new {
my $class = shift;
# Some private stuff for ourself
my $self = {
jsonqueue => [],
buffer => "",
iChar => [],
};
# We are interested in characters of this code
$self->{iChar}->[91] = 1;
$self->{iChar}->[93] = 1;
$self->{iChar}->[123] = 1;
$self->{iChar}->[125] = 1;
# Go with god my son
bless $self, $class;
return $self;
}
=head1 Block functions
=head2 digest
Digest the text that is fed in and attempt to return a complete an array of JSON object from it, returns either a blank array or an array of text-encoded-json.
Note you can call and use this at any time, even if you are using streaming functionality.
=cut
sub digest {
my $self = shift;
my $data = shift;
# A place for our return
my $return = [];
# If we got passed a blank data scalar just return failure
return $return if (!$data);
# Save the current state for if we are dealing with a stream elsewhere.
my $stateBackup = $self->{state} if ($self->{state});
# Start with a fresh state
$self->reset;
# Load the digest data into the processor
$self->process($data);
# Generate our results
while ($self->stack) { push @{$return},$self->pull }
# Restore the previous state
$self->{state} = $stateBackup if ($stateBackup);
# Process the data and return the result
return $return;
}
=head1 Streaming functions
=head2 process
Load data into the buffer for json extraction, can be called at any point.
This function will return the buffer length remaining after extraction has been attempted.
This function takes 1 optional argument, text to be added to the buffer.
=cut
sub process {
my $self = shift;
my $data = shift;
# Add any data present to the buffer, elsewhere return the length of what we have.
if ($data) { $self->{buffer} .= $data }
else { return length($self->{buffer}) }
# If we have no buffer return 0.
if (!$self->{buffer}) { return 0 }
# Load our state or establish a new one
my $state;
if ( $self->{state} ) {
$state = $self->{state};
} else {
( run in 1.054 second using v1.01-cache-2.11-cpan-5a3173703d6 )