BSON

 view release on metacpan or  search on metacpan

lib/BSON.pm  view on Meta::CPAN

use 5.010001;
use strict;
use warnings;

package BSON;
# ABSTRACT: BSON serialization and deserialization (EOL)

use base 'Exporter';
our @EXPORT_OK = qw/encode decode/;

use version;
our $VERSION = 'v1.12.2';

use Carp;
use Config;
use Scalar::Util qw/blessed looks_like_number/;

use Moo 2.002004; # safer generated code
use boolean;
use BSON::OID;

use constant {
    HAS_INT64 => $Config{use64bitint},
    HAS_LD    => $Config{uselongdouble},
};

use if !HAS_INT64, "Math::BigInt";

my $bools_re = qr/::(?:Boolean|_Bool|Bool)\z/;

use namespace::clean -except => 'meta';

my $max_int32 = 2147483647;

# Dependency-free equivalent of what we need from Module::Runtime
sub _try_load {
    my ( $mod, $ver ) = @_;
    ( my $file = "$mod.pm" ) =~ s{::}{/}g;
    my $load = eval { require $file; $mod->VERSION($ver) if defined $ver; 1 };
    delete $INC{$file} if !$load; # for old, broken perls
    die $@ if !$load;
    return 1;
}

BEGIN {
    my ($class, @errs);
    if ( $class = $ENV{PERL_BSON_BACKEND} ) {
        eval { _try_load($class) };
        if ( my $err = $@ ) {
            $err =~ s{ at \S+ line .*}{};
            die "Error: PERL_BSON_BACKEND '$class' could not be loaded: $err\n";
        }
        unless ($class->can("_encode_bson") && $class->can("_decode_bson") ) {
            die "Error: PERL_BSON_BACKEND '$class' does not implement the correct API.\n";
        }
    }
    elsif ( eval { _try_load( $class = "BSON::XS" ) } or do { push @errs, $@; 0 } ) {
        # module loaded; nothing else to do
    }
    elsif ( eval { _try_load( $class = "BSON::PP" ) } or do { push @errs, $@; 0 } ) {
        # module loaded; nothing else to do
    }
    else {
        s/\n/ /g for @errs;
        die join( "\n* ", "Error: Couldn't load a BSON backend:", @errs ) . "\n";
    }

    *_encode_bson = $class->can("_encode_bson");
    *_decode_bson = $class->can("_decode_bson");
    *_backend_class = sub { $class }; # for debugging
}

# LOAD AFTER XS/PP, so that modules can pick up right version of helpers
use BSON::Types (); # loads types for extjson inflation

#--------------------------------------------------------------------------#

lib/BSON.pm  view on Meta::CPAN

    isa     => sub { die "not a string" if ! defined $_[0] || ref $_[0] },
);

#pod =attr max_length
#pod
#pod This attribute defines the maximum document size. The default is 0, which
#pod disables any maximum.
#pod
#pod If set to a positive number, it applies to both encoding B<and> decoding (the
#pod latter is necessary for prevention of resource consumption attacks).
#pod
#pod =cut

has max_length => (
    is      => 'ro',
    isa     => sub { die "not a non-negative number" unless defined $_[0] && $_[0] >= 0 },
);

#pod =attr op_char
#pod
#pod This is a single character to use for special MongoDB-specific query
#pod operators.  If a key starts with C<op_char>, the C<op_char> character will
#pod be replaced with "$".
#pod
#pod The default is "$", meaning that no replacement is necessary.
#pod
#pod =cut

has op_char => (
    is  => 'ro',
    isa => sub { die "not a single character" if defined $_[0] && length $_[0] > 1 },
);

#pod =attr ordered
#pod
#pod If set to a true value, then decoding will return a reference to a tied
#pod hash that preserves key order. Otherwise, a regular (unordered) hash
#pod reference will be returned.
#pod
#pod B<IMPORTANT CAVEATS>:
#pod
#pod =for :list
#pod * When 'ordered' is true, users must not rely on the return value being any
#pod   particular tied hash implementation.  It may change in the future for
#pod   efficiency.
#pod * Turning this option on entails a significant speed penalty as tied hashes
#pod   are slower than regular Perl hashes.
#pod
#pod The default is false.
#pod
#pod =cut

has ordered => (
    is => 'ro',
);

#pod =attr prefer_numeric
#pod
#pod When false, scalar values will be encoded as a number if they were
#pod originally a number or were ever used in a numeric context.  However, a
#pod string that looks like a number but was never used in a numeric context
#pod (e.g. "42") will be encoded as a string.
#pod
#pod If C<prefer_numeric> is set to true, the encoder will attempt to coerce
#pod strings that look like a number into a numeric value.  If the string
#pod doesn't look like a double or integer, it will be encoded as a string.
#pod
#pod B<IMPORTANT CAVEAT>: the heuristics for determining whether something is a
#pod string or number are less accurate on older Perls.  See L<BSON::Types>
#pod for wrapper classes that specify exact serialization types.
#pod
#pod The default is false.
#pod
#pod =cut

has prefer_numeric => (
    is => 'ro',
);

#pod =attr wrap_dbrefs
#pod
#pod If set to true, during decoding, documents with the fields C<'$id'> and
#pod C<'$ref'> (literal dollar signs, not variables) will be wrapped as
#pod L<BSON::DBRef> objects.  If false, they are decoded into ordinary hash
#pod references (or ordered hashes, if C<ordered> is true).
#pod
#pod The default is true.
#pod
#pod =cut

has wrap_dbrefs  => (
    is => 'ro',
);

#pod =attr wrap_numbers
#pod
#pod If set to true, during decoding, numeric values will be wrapped into
#pod BSON type-wrappers: L<BSON::Double>, L<BSON::Int64> or L<BSON::Int32>.
#pod While very slow, this can help ensure fields can round-trip if unmodified.
#pod
#pod The default is false.
#pod
#pod =cut

has wrap_numbers => (
    is => 'ro',
);

#pod =attr wrap_strings
#pod
#pod If set to true, during decoding, string values will be wrapped into a BSON
#pod type-wrappers, L<BSON::String>.  While very slow, this can help ensure
#pod fields can round-trip if unmodified.
#pod
#pod The default is false.
#pod
#pod =cut

has wrap_strings => (
    is => 'ro',
);

lib/BSON.pm  view on Meta::CPAN


    for my $k ( keys %$hash ) {
        my $v = $hash->{$k};
        if ( substr( $k, 0, 1 ) eq '$' ) {
            croak "Dollar-prefixed key '$k' is not legal in top-level hash";
        }
        my $type = ref($v);
        $hash->{$k} =
            $type eq 'HASH'    ? $self->_inflate_hash($v)
          : $type eq 'ARRAY'   ? $self->_inflate_array($v)
          : $type =~ $bools_re ? ( $v ? true : false )
          :                      $v;
    }

    return $hash;
}

#pod =method perl_to_extjson
#pod
#pod     use JSON::MaybeXS;
#pod     my $ext = BSON->perl_to_extjson($data, \%options);
#pod     my $json = encode_json($ext);
#pod
#pod Takes a perl data structure (i.e. hashref) and turns it into an
#pod L<MongoDB Extended JSON|https://github.com/mongodb/specifications/blob/master/source/extended-json.rst>
#pod structure. Note that the structure will still have to be serialized.
#pod
#pod Possible options are:
#pod
#pod =for :list
#pod * C<relaxed> A boolean indicating if "relaxed extended JSON" should
#pod be generated. If not set, the default value is taken from the
#pod C<BSON_EXTJSON_RELAXED> environment variable.
#pod
#pod =cut

my $use_win32_specials = ($^O eq 'MSWin32' && $] lt "5.022");

my $is_inf = $use_win32_specials ? qr/^1.\#INF/i : qr/^inf/i;
my $is_ninf = $use_win32_specials ? qr/^-1.\#INF/i : qr/^-inf/i;
my $is_nan = $use_win32_specials ? qr/^-?1.\#(?:IND|QNAN)/i : qr/^-?nan/i;

sub perl_to_extjson {
    my ($class, $data, $options) = @_;

    local $ENV{BSON_EXTJSON} = 1;
    local $ENV{BSON_EXTJSON_RELAXED} = $ENV{BSON_EXTJSON_RELAXED};
    $ENV{BSON_EXTJSON_RELAXED} = $options->{relaxed};

    if (not defined $data) {
        return undef; ## no critic
    }

    if (blessed($data) and $data->can('TO_JSON')) {
        my $json_data = $data->TO_JSON;
        return $json_data;
    }

    if (not ref $data) {

        if (looks_like_number($data)) {
            if ($ENV{BSON_EXTJSON_RELAXED}) {
                return $data;
            }

            if ($data =~ m{\A-?[0-9_]+\z}) {
                if ($data <= $max_int32) {
                    return { '$numberInt' => "$data" };
                }
                else {
                    return { '$numberLong' => "$data" };
                }
            }
            else {
                return { '$numberDouble' => 'Infinity' }
                    if $data =~ $is_inf;
                return { '$numberDouble' => '-Infinity' }
                    if $data =~ $is_ninf;
                return { '$numberDouble' => 'NaN' }
                    if $data =~ $is_nan;
                my $value = "$data";
                $value = $value / 1.0;
                return { '$numberDouble' => "$value" };
            }
        }

        return $data;
    }

    if (boolean::isBoolean($data)) {
        return $data;
    }

    if (ref $data eq 'HASH') {
        for my $key (keys %$data) {
            my $value = $data->{$key};
            $data->{$key} = $class->perl_to_extjson($value, $options);
        }
        return $data;
    }

    if (ref $data eq 'ARRAY') {
        for my $index (0 .. $#$data) {
            my $value = $data->[$index];
            $data->[$index] = $class->perl_to_extjson($value, $options);
        }
        return $data;
    }

    if (blessed($data) and $data->isa('JSON::PP::Boolean')) {
        return $data;
    }

    if (
        blessed($data) and (
            $data->isa('Math::BigInt') or
            $data->isa('Math::BigFloat')
        )
    ) {
        return $data;
    }

lib/BSON.pm  view on Meta::CPAN

a reference to the problematic document or byte-string

=item *

the method in which the error occurred (e.g. C<encode_one> or C<decode_one>)

=back

Note: for decoding errors, the byte-string is passed as a reference to avoid
copying possibly large strings.

If not provided, errors messages will be thrown with C<Carp::croak>.

=head2 invalid_chars

A string containing ASCII characters that must not appear in keys.  The default
is the empty string, meaning there are no invalid characters.

=head2 max_length

This attribute defines the maximum document size. The default is 0, which
disables any maximum.

If set to a positive number, it applies to both encoding B<and> decoding (the
latter is necessary for prevention of resource consumption attacks).

=head2 op_char

This is a single character to use for special MongoDB-specific query
operators.  If a key starts with C<op_char>, the C<op_char> character will
be replaced with "$".

The default is "$", meaning that no replacement is necessary.

=head2 ordered

If set to a true value, then decoding will return a reference to a tied
hash that preserves key order. Otherwise, a regular (unordered) hash
reference will be returned.

B<IMPORTANT CAVEATS>:

=over 4

=item *

When 'ordered' is true, users must not rely on the return value being any particular tied hash implementation.  It may change in the future for efficiency.

=item *

Turning this option on entails a significant speed penalty as tied hashes are slower than regular Perl hashes.

=back

The default is false.

=head2 prefer_numeric

When false, scalar values will be encoded as a number if they were
originally a number or were ever used in a numeric context.  However, a
string that looks like a number but was never used in a numeric context
(e.g. "42") will be encoded as a string.

If C<prefer_numeric> is set to true, the encoder will attempt to coerce
strings that look like a number into a numeric value.  If the string
doesn't look like a double or integer, it will be encoded as a string.

B<IMPORTANT CAVEAT>: the heuristics for determining whether something is a
string or number are less accurate on older Perls.  See L<BSON::Types>
for wrapper classes that specify exact serialization types.

The default is false.

=head2 wrap_dbrefs

If set to true, during decoding, documents with the fields C<'$id'> and
C<'$ref'> (literal dollar signs, not variables) will be wrapped as
L<BSON::DBRef> objects.  If false, they are decoded into ordinary hash
references (or ordered hashes, if C<ordered> is true).

The default is true.

=head2 wrap_numbers

If set to true, during decoding, numeric values will be wrapped into
BSON type-wrappers: L<BSON::Double>, L<BSON::Int64> or L<BSON::Int32>.
While very slow, this can help ensure fields can round-trip if unmodified.

The default is false.

=head2 wrap_strings

If set to true, during decoding, string values will be wrapped into a BSON
type-wrappers, L<BSON::String>.  While very slow, this can help ensure
fields can round-trip if unmodified.

The default is false.

=head2 dt_type (Discouraged)

Sets the type of object which is returned for BSON DateTime fields. The
default is C<undef>, which returns objects of type L<BSON::Time>.  This is
overloaded to be the integer epoch value when used as a number or string,
so is somewhat backwards compatible with C<dt_type> in the L<MongoDB>
driver.

Other acceptable values are L<BSON::Time> (explicitly), L<DateTime>,
L<Time::Moment>, L<DateTime::Tiny>, L<Mango::BSON::Time>.

Because BSON::Time objects have methods to convert to DateTime,
Time::Moment or DateTime::Tiny, use of this field is discouraged.  Users
should use these methods on demand.  This option is provided for backwards
compatibility only.

=head1 METHODS

=head2 encode_one

    $byte_string = $codec->encode_one( $doc );
    $byte_string = $codec->encode_one( $doc, \%options );

lib/BSON.pm  view on Meta::CPAN

    BSON::Bytes                 0x05 BINARY         BSON::Bytes
    scalarref
    BSON::Binary[d]
    MongoDB::BSON::Binary[d]
    -------------------------------------------------------------------
    n/a                         0x06 UNDEFINED[d]   undef
    -------------------------------------------------------------------
    BSON::OID                   0x07 OID            BSON::OID
    BSON::ObjectId[d]
    MongoDB::OID[d]
    -------------------------------------------------------------------
    boolean                     0x08 BOOL           boolean
    BSON::Bool[d]
    JSON::XS::Boolean
    JSON::PP::Boolean
    JSON::Tiny::_Bool
    Mojo::JSON::_Bool
    Cpanel::JSON::XS::Boolean
    Types::Serialiser::Boolean
    -------------------------------------------------------------------
    BSON::Time                  0x09 DATE_TIME      BSON::Time
    DateTime
    DateTime::Tiny
    Time::Moment
    Mango::BSON::Time
    -------------------------------------------------------------------
    undef                       0x0a NULL           undef
    -------------------------------------------------------------------
    BSON::Regex                 0x0b REGEX          BSON::Regex
    qr// reference
    MongoDB::BSON::Regexp[d]
    -------------------------------------------------------------------
    n/a                         0x0c DBPOINTER[d]   BSON::DBRef
    -------------------------------------------------------------------
    BSON::Code[6]               0x0d CODE           BSON::Code
    MongoDB::Code[6]
    -------------------------------------------------------------------
    n/a                         0x0e SYMBOL[d]      string
    -------------------------------------------------------------------
    BSON::Code[6]               0x0f CODEWSCOPE     BSON::Code
    MongoDB::Code[6]
    -------------------------------------------------------------------
    integer[7][8]               0x10 INT32          integer[2]
    BSON::Int32
    -------------------------------------------------------------------
    BSON::Timestamp             0x11 TIMESTAMP      BSON::Timestamp
    MongoDB::Timestamp[d]
    -------------------------------------------------------------------
    integer[7]                  0x12 INT64          integer[2][9]
    BSON::Int64
    Math::BigInt
    Math::Int64
    -------------------------------------------------------------------
    BSON::MaxKey                0x7F MAXKEY         BSON::MaxKey
    MongoDB::MaxKey[d]
    -------------------------------------------------------------------
    BSON::MinKey                0xFF MINKEY         BSON::MinKey
    MongoDB::MinKey[d]

    [d] Deprecated or soon to be deprecated.
    [1] Scalar with "NV" internal representation or a string that looks
        like a float if the 'prefer_numeric' option is true.
    [2] If the 'wrap_numbers' option is true, numeric types will be wrapped
        as BSON::Double, BSON::Int32 or BSON::Int64 as appropriate to ensure
        round-tripping. If the 'wrap_strings' option is true, strings will
        be wrapped as BSON::String, likewise.
    [3] Scalar without "NV" or "IV" representation and not identified as a
        number by notes [1] or [7].
    [4] If 'ordered' option is set, will return a tied hash that preserves
        order (deprecated 'ixhash' option still works).
    [5] If the document appears to contain a DBRef and a 'dbref_callback'
        exists, that callback is executed with the deserialized document.
    [6] Code is serialized as CODE or CODEWSCOPE depending on whether a
        scope hashref exists in BSON::Code/MongoDB::Code.
    [7] Scalar with "IV" internal representation or a string that looks like
        an integer if the 'prefer_numeric' option is true.
    [8] Only if the integer fits in 32 bits.
    [9] On 32-bit platforms, 64-bit integers are deserialized to
        Math::BigInt objects (even if subsequently wrapped into
        BSON::Int64 if 'wrap_scalars' is true).

=head1 THREADS

Threads are never recommended in Perl, but this module is thread safe.

=head1 ENVIRONMENT

=over 4

=item *

PERL_BSON_BACKEND – if set at compile time, this will be treated as a module name.  The module will be loaded and used as the BSON backend implementation.  It must implement the same API as C<BSON::PP>.

=item *

BSON_EXTJSON - if set, serializing BSON type wrappers via C<TO_JSON> will produce Extended JSON v2 output.

=item *

BSON_EXTJSON_RELAXED - if producing Extended JSON output, if this is true, values will use the "Relaxed" form of Extended JSON, which sacrifices type round-tripping for improved human readability.

=back

=head1 SEMANTIC VERSIONING SCHEME

Starting with BSON C<v0.999.0>, this module is using a "tick-tock"
three-part version-tuple numbering scheme: C<vX.Y.Z>

=over 4

=item *

In stable releases, C<X> will be incremented for incompatible API changes.

=item *

Even-value increments of C<Y> indicate stable releases with new functionality.  C<Z> will be incremented for bug fixes.

=item *

Odd-value increments of C<Y> indicate unstable ("development") releases that should not be used in production.  C<Z> increments have no semantic meaning; they indicate only successive development releases.  Development releases may have API-breaking ...

=back

=head1 HISTORY AND ROADMAP

This module was originally written by Stefan G.  In 2014, he graciously
transferred ongoing maintenance to MongoDB, Inc.

The C<bson_xxxx> helper functions in L<BSON::Types> were inspired by similar
work in L<Mango::BSON> by Sebastian Riedel.

=head1 AUTHORS

=over 4



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