JSON-SL

 view release on metacpan or  search on metacpan

lib/JSON/SL/Tuba.pm  view on Meta::CPAN

    if ($mode !~ m{[\=\~\?\#"]}) {
        die("Invalid type '$mode'. Mode must be one of [^#\"]");
    }
    return $tuba->_ax_opt(ord("$mode"))
}

sub accum_all {
    my ($tuba,$boolean) = @_;
    if (@_ != 2) {
        die("Must have boolean argument!");
    }
    my %opts = map {
        $_, $boolean
    } ('=','~','#','?','"');
    $tuba->accum(%opts);
}

#build convenience methods:
foreach (['key', '#'],
         ['string', '"'],
         ['number', '='],
         ['boolean', '?'],
         ['null', '~']
         ) {

    my ($mode,$sym) = @$_;
    no strict 'refs';
    *{"accum_$mode"} = sub {
        my ($tuba,$bool) = @_;
        $tuba->_ax_opt(ord($sym), $bool);
    }
}

1;

__END__


=head1 NAME

JSON::SL::Tuba - High performance SAX-like interface for JSON

=head1 SYNOPSIS

Create a very naive JSON encoder using JSON::SL::Tuba

    my $JSON ||= <<'EOJ';
    {
    "a" : "b",
    "c" : { "d" : "e" },
    "f" : [ "g", "h", "i", "j" ],
    "a number" : 0.4444444444,
    "a (false) boolean": false,
    "another (true) boolean" : true,
    "a null value" : null,
    "exponential" : 1.3413400E4,
    "an\tescaped key" : "a u-\u0065\u0073caped value", "שלום":"להראות"
    }
    EOJ

    # Split the 'stream' into multiple chunks to demonstrate the streaming
    # feature:
    my @Chunks = unpack("a(8)*", $JSON);

    # Make a subclass and set up the methods..

    package A::Giant::Tuba;
    use base qw(JSON::SL::Tuba);

    sub on_any {
        my ($tuba,$info,$data) = @_;

        #use constant comparisons
        if ($info->{Type} == TUBA_TYPE_JSON) {
            printf STDERR ("JSON DOCUMENT: %c\n\n", $info->{Mode});
            return;
        }

        # or use the mnemonic ones
        if ($info->{Key} && $info->{Mode} =~ m,[>\+],) {
            printf ('"%s" : ', $info->{Key});
        }

        if ($info->{Type} == TUBA_TYPE_STRING) {
            printf('"%s",' . "\n", $data || "<NO DATA>");

        } elsif ($info->{Type} =~ m,[\[\{],) {
            if ($info->{Mode} eq '+') {
                print $info->{Type} . "\n";
            } else {

                print $JSON::SL::Tuba::CloseTokens{$info->{Type}} . ",\n";
            }
        } else {
            if (defined $data) {
                print $data . ",\n"
            } else {
                die ("hrrm.. what have we here?")
                    unless $info->{Type} == TUBA_TYPE_NULL;
                print "null,\n";
            }
        }
    }

    my $o = My::Giant::Tuba->new();
    $o->parse($_) for @Chunks;

Output:

    JSON DOCUMENT: +

    {
    "a" : b,
    "c" : {
    "d" : e,
    },
    "f" : [
    g,
    h,
    i,
    j,
    ],
    "a number" : 0.4444444444,
    "a (false) boolean" : 0,
    "another (true) boolean" : 1,
    "a null value" : null
    "exponential" : 13413.4,
    "an	escaped key" : a u-escaped value,
    "שלום" : להראות,
    },
    JSON DOCUMENT: -

=head1 DESCRIPTION

C<JSON::SL::Tuba> provides an event-based and high performance SAX-like interface
for parsing streaming JSON.

Emphasis when designing C<JSON::SL::Tuba> was the reduction of boilerplate (the
author does not have favorable experiences with SAX APIs) and high performance.

This uses the same core JSON functionality and speed as L<JSON::SL>.

To use C<JSON::SL::Tuba>, simply inherit from it and define one or more
methods to be called when a parse event occurs.

In normal cases (and this is the default), only a single method (see below)
needs to be implemented to be able to receive events.

Of course, if your application requirements are more complex, Tuba is able
to deliver you events to the resolution of a single character.

=head2 CALLBACK ARGUMENTS AND TERMINOLOGY

These are the list of methods which to implement. All methods follow a single
unified calling convention in the form of

    callback($tuba, $info, $data);

where C<$tuba> is the C<JSON::SL::Tuba> instance, C<$info> is a hash reference
containing metadata about the item for which the event was received, and C<$data>
contains the actual 'data' (if applicable)

=head3 Info Hash

This hash contains metadata for determining relevant information about the current
item.

The hash and all its contents are read-only. Their contents are not valid after
the callback returns (see L</CAVEATS>). This is for both performance and
sanity reasons.
    

Its keys and values are as follows

=over

=item C<Type>

This is the type of JSON object for which an event was received.

The following table represents a table of type constants, and their mnemonic
symbols. The value to this key itself is a double-typed scalar which yields either
the character or the numeric value depending on the context.

    Constant            Mnemonic Symbol     Description
    
    === Scalar Types ===
    TUBA_TYPE_STRING        "               "string" value
    TUBA_TYPE_KEY           #               hash key
    TUBA_TYPE_BOOLEAN       ?               JSON boolean atom ('true','false')
    TUBA_TYPE_NUMBER        =               number
    TUBA_TYPE_NULL          ~               JSON 'null' atom
    
    === Container Types ===
    TUBA_TYPE_OBJECT        {               hash (JSON 'object')
    TUBA_TYPE_LIST          [               array (JSON 'list')



( run in 0.422 second using v1.01-cache-2.11-cpan-d7f47b0818f )