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 )