BSON
view release on metacpan or search on metacpan
lib/BSON.pm view on Meta::CPAN
#pod =method create_oid
#pod
#pod $oid = BSON->create_oid;
#pod
#pod This class method returns a new L<BSON::OID>. This abstracts OID
#pod generation away from any specific Object ID class and makes it an interface
#pod on a BSON codec. Alternative BSON codecs should define a similar class
#pod method that returns an Object ID of whatever type is appropriate.
#pod
#pod =cut
sub create_oid { return BSON::OID->new }
#pod =method inflate_extjson (DEPRECATED)
#pod
#pod This legacy method does not follow the L<MongoDB Extended JSON|https://github.com/mongodb/specifications/blob/master/source/extended-json.rst>
#pod specification.
#pod
#pod Use L</extjson_to_perl> instead.
#pod
#pod =cut
sub inflate_extjson {
my ( $self, $hash ) = @_;
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);
lib/BSON.pm view on Meta::CPAN
);
}
if ( exists $data->{'$regex'} and not ref $data->{'$regex'}) {
return BSON::Regex->new(
pattern => $data->{'$regex'},
( exists $data->{'$options'} ? ( flags => $data->{'$options'} ) : () ),
);
}
if ( exists $data->{'$regularExpression'} ) {
my $value = $data->{'$regularExpression'};
return BSON::Regex->new(
pattern => $value->{pattern},
( exists $value->{options} ? ( flags => $value->{options} ) : () ),
);
}
if ( exists $data->{'$code'} ) {
return BSON::Code->new(
code => $data->{'$code'},
( exists $data->{'$scope'}
? ( scope => $class->_extjson_to_perl($data->{'$scope'}) )
: ()
),
);
}
if ( exists $data->{'$undefined'} ) {
return undef; ## no critic
}
if ( exists $data->{'$dbPointer'} ) {
my $data = $data->{'$dbPointer'};
my $id = $data->{'$id'};
$id = $class->_extjson_to_perl($id) if ref($id) eq 'HASH';
return BSON::DBPointer->new(
'$ref' => $data->{'$ref'},
'$id' => $id,
);
}
if ( exists $data->{'$ref'} ) {
my $id = delete $data->{'$id'};
$id = $class->_extjson_to_perl($id) if ref($id) eq 'HASH';
return BSON::DBRef->new(
'$ref' => delete $data->{'$ref'},
'$id' => $id,
'$db' => delete $data->{'$db'},
%$data, # extra
);
}
if ( exists $data->{'$numberDecimal'} ) {
return BSON::Decimal128->new( value => $data->{'$numberDecimal'} );
}
# Following extended JSON is non-standard
if ( exists $data->{'$numberDouble'} ) {
if ( $data->{'$numberDouble'} eq '-0' && $] lt '5.014' && ! HAS_LD ) {
$data->{'$numberDouble'} = '-0.0';
}
return BSON::Double->new( value => $data->{'$numberDouble'} );
}
if ( exists $data->{'$symbol'} ) {
return BSON::Symbol->new(value => $data->{'$symbol'});
}
for my $key (keys %$data) {
my $value = $data->{$key};
$data->{$key} = $class->_extjson_to_perl($value);
}
return $data;
}
if (ref $data eq 'ARRAY') {
for my $index (0 .. $#$data) {
my $value = $data->[$index];
$data->[$index] = ref($value)
? $class->_extjson_to_perl($value)
: $value;
}
return $data;
}
return $data;
}
#--------------------------------------------------------------------------#
# legacy functional interface
#--------------------------------------------------------------------------#
#pod =func encode
#pod
#pod my $bson = encode({ bar => 'foo' }, \%options);
#pod
#pod This is the legacy, functional interface and is only exported on demand.
#pod It takes a hashref and returns a BSON string.
#pod It uses an internal codec singleton with default attributes.
#pod
#pod =func decode
#pod
#pod my $hash = decode( $bson, \%options );
#pod
#pod This is the legacy, functional interface and is only exported on demand.
#pod It takes a BSON string and returns a hashref.
#pod It uses an internal codec singleton with default attributes.
#pod
#pod =cut
{
my $CODEC;
sub encode {
if ( defined $_[0] && ( $_[0] eq 'BSON' || ( blessed($_[0]) && $_[0]->isa('BSON') ) ) ) {
Carp::croak("Error: 'encode' is a function, not a method");
}
my $doc = shift;
$CODEC = BSON->new unless defined $CODEC;
lib/BSON.pm view on Meta::CPAN
if ( exists $hash->{'$binary'} ) {
require MIME::Base64;
return BSON::Bytes->new(
data => MIME::Base64::decode_base64($hash->{'$binary'}),
subtype => hex( $hash->{'$type'} || 0 )
);
}
if ( exists $hash->{'$date'} ) {
my $v = $hash->{'$date'};
$v = ref($v) eq 'HASH' ? BSON->_inflate_hash($v) : _iso8601_to_epochms($v);
return BSON::Time->new( value => $v );
}
if ( exists $hash->{'$minKey'} ) {
return BSON::MinKey->new;
}
if ( exists $hash->{'$maxKey'} ) {
return BSON::MaxKey->new;
}
if ( exists $hash->{'$timestamp'} ) {
return BSON::Timestamp->new(
seconds => $hash->{'$timestamp'}{t},
increment => $hash->{'$timestamp'}{i},
);
}
if ( exists $hash->{'$regex'} ) {
return BSON::Regex->new(
pattern => $hash->{'$regex'},
( exists $hash->{'$options'} ? ( flags => $hash->{'$options'} ) : () ),
);
}
if ( exists $hash->{'$code'} ) {
return BSON::Code->new(
code => $hash->{'$code'},
( exists $hash->{'$scope'} ? ( scope => $hash->{'$scope'} ) : () ),
);
}
if ( exists $hash->{'$undefined'} ) {
return undef; ## no critic
}
if ( exists $hash->{'$ref'} ) {
my $id = $hash->{'$id'};
$id = BSON->_inflate_hash($id) if ref($id) eq 'HASH';
return BSON::DBRef->new( '$ref' => $hash->{'$ref'}, '$id' => $id );
}
if ( exists $hash->{'$numberDecimal'} ) {
return BSON::Decimal128->new( value => $hash->{'$numberDecimal'} );
}
# Following extended JSON is non-standard
if ( exists $hash->{'$numberDouble'} ) {
if ( $hash->{'$numberDouble'} eq '-0' && $] lt '5.014' && ! HAS_LD ) {
$hash->{'$numberDouble'} = '-0.0';
}
return BSON::Double->new( value => $hash->{'$numberDouble'} );
}
if ( exists $hash->{'$symbol'} ) {
return $hash->{'$symbol'};
}
return $hash;
}
sub _inflate_array {
my ($class, $array) = @_;
if (@$array) {
for my $i ( 0 .. $#$array ) {
my $v = $array->[$i];
$array->[$i] =
ref($v) eq 'HASH' ? BSON->_inflate_hash($v)
: ref($v) eq 'ARRAY' ? _inflate_array($v)
: $v;
}
}
return $array;
}
my $iso8601_re = qr{
(\d{4}) - (\d{2}) - (\d{2}) T # date
(\d{2}) : (\d{2}) : ( \d+ (?:\. \d+ )? ) # time
(?: Z | ([+-] \d{2} :? (?: \d{2} )? ) )? # maybe TZ
}x;
sub _iso8601_to_epochms {
my ($date) = shift;
require Time::Local;
my $zone_offset = 0;;
if ( substr($date,-1,1) eq 'Z' ) {
chop($date);
}
if ( $date =~ /\A$iso8601_re\z/ ) {
my ($Y,$M,$D,$h,$m,$s,$z) = ($1,$2-1,$3,$4,$5,$6,$7);
if (defined($z) && length($z)) {
$z =~ tr[:][];
$z .= "00" if length($z) < 5;
my $zd = substr($z,0,1);
my $zh = substr($z,1,2);
my $zm = substr($z,3,2);
$zone_offset = ($zd eq '-' ? -1 : 1 ) * (3600 * $zh + 60 * $zm);
}
my $frac = $s - int($s);
my $epoch = Time::Local::timegm(int($s), $m, $h, $D, $M, $Y) - $zone_offset;
$epoch = HAS_INT64 ? 1000 * $epoch : Math::BigInt->new($epoch) * 1000;
$epoch += HAS_INT64 ? $frac * 1000 : Math::BigFloat->new($frac) * 1000;
return $epoch;
}
else {
Carp::croak("Couldn't parse '\$date' field: $date\n");
}
( run in 1.400 second using v1.01-cache-2.11-cpan-364913b4093 )