Десериализация из BSON

Внимание

Документы BSON технически могут содержать повторяющиеся ключи, поскольку документы хранятся в виде списка пар ключ-значение; однако приложениям следует воздерживаться от создания документов с дубликатами ключей, поскольку поведение сервера и драйвера может быть неопределённым. Поскольку объекты и массивы PHP не могут иметь повторяющихся ключей, данные также могут быть потеряны при декодировании документа BSON с повторяющимися ключами.

Устаревший модуль mongo десериализовал как документы, так и массивы BSON в качестве массивов PHP. Хотя с PHP массивами удобно работать, такое поведение было проблематичным, поскольку различные типы BSON могут десериализоваться до одного и того же значения PHP (например, {"0": "foo"} и ["foo"]) и будет невозможно вывести оригинальный тип BSON. По умолчанию текущий драйвер решает эту проблему, обеспечивая преобразование массивов и документов BSON в массивы и объекты PHP соответственно.

Для составных типов существует три типа данных:

root

относится только к документу верхнего уровня BSON

document

относится только к встроенным документам BSON

array

относится к массивам BSON

Помимо трёх групповых типов, также можно настроить определённые поля в документе для сопоставления с типами данных, указанными ниже. В качестве примера, следующая карта типов позволяет сопоставить каждый встроенный документ в массиве "addresses" с классом Address, а каждое поле "city" в этих документах с встроенным адресом с классом City:

[
    'fieldPaths' => [
        'addresses.$' => 'MyProject\Address',
        'addresses.$.city' => 'MyProject\City',
    ],
]

Каждый из этих трёх типов данных, а также сопоставления для конкретных полей могут быть сопоставлены с различными типами PHP. Возможные значения сопоставления:

не указано или NULL (по умолчанию)

  • Массив BSON будет десериализован, как PHP array.

  • Документ BSON (корневой или внедрённый) без свойства __pclass [1] становится объектом PHP stdClass, причём каждый ключ документа BSON устанавливается в качестве открытого свойства stdClass.

  • Документ BSON (корневой или встроенный) со свойством __pclass [1] становится объектом PHP имени класса, как это определено свойством __pclass.

    Если указанный класс реализует интерфейс MMongoDB\BSON\Persistable, то свойства документа BSON, включая свойство __pclass, отправляются в виде ассоциативного массива в функцию MongoDB\BSON\Unserializable::bsonUnserialize() для инициализации свойств объекта.

    Если названный класс не существует или не реализует интерфейс MongoDB\BSON\Persistable, будет использоваться stdClass, и каждый ключ документа BSON (включая __pclass) будет установлен, как открытое свойство stdClass.

    Функциональность __pclass зависит от того, является ли свойство частью извлечённого документа MongoDB. Если вы используете проекцию при запросе документов, вам нужно включить поле __pclass в проекцию, чтобы эта функция работала.

"array"

Превращает массив BSON или документ BSON в массив PHP. Не будет специальной обработки свойства __pclass [1], но его можно установить, как элемент в возвращаемом массиве, если он присутствовал в документе BSON.

"object" или "stdClass"

Превращает массив BSON или документ BSON в объект stdClass. Не будет специальной обработки свойства __pclass [1], но оно может быть установлено, как открытое свойство в возвращаемом объекте, если оно присутствовало в документе BSON.

"bson"

Превращает BSON массив в MongoDB\BSON\PackedArray и BSON документ в MongoDB\BSON\Document, независимо от того, есть ли у BSON документа свойство __pclass [1].

Замечание: Значение bson доступно только для трёх корневых типов, но не в отображениях для конкретных полей.

любая другая строка

Определяет имя класса, который должен десериализовать массив BSON или объект BSON. Для объектов BSON, которые содержат свойства __pclass, этот класс будет иметь приоритет.

Если названный класс не существует, не является конкретным (то есть является абстрактным или интерфейсом) или не реализует MongoDB\BSON\Unserializable, то выдаётся исключение MongoDB\Driver\Exception\InvalidArgumentException.

Если объект BSON имеет свойство __pclass, и этот класс существует и реализует MongoDB\BSON\Persistable, он заменит класс, представленный в карте типов.

Свойства документа BSON, включая свойство __pclass, если оно существует, будут отправлены в виде ассоциативного массива в функцию MongoDB\BSON\Unserializable::bsonUnserialize() для инициализации свойств объекта.

TypeMaps

TypeMaps можно установить с помощью метода MongoDB\Driver\Cursor::setTypeMap() для объекта MongoDB\Driver\Cursor или аргумента $typeMap в MongoDB\BSON\toPHP(), MongoDB\BSON\Document::toPHP() и MongoDB\BSON\PackedArray::toPHP(). Каждый из трёх классов (root, document, и array) может быть задан индивидуально, в дополнение к типам полей.

Если значение на карте равно NULL, это означает то же самое, что и значение по умолчанию для этого элемента.

Примеры

В этих примерах используются следующие классы:

MyClass

который не реализует интерфейс

YourClass

который реализует MongoDB\BSON\Unserializable

OurClass

который реализует MongoDB\BSON\Persistable

TheirClass

который расширяет OurClass

Метод MongoDB\BSON\Unserializable::bsonUnserialize() класса YourClass, OurClass, OurClass выполняет итерацию по массиву и устанавливает свойства без изменений. Он также устанавливает для свойства $unserialized значение true:

<?php

function bsonUnserialize( array $map )
{
foreach (
$map as $k => $value )
{
$this->$k = $value;
}
$this->unserialized = true;
}

/* typemap: [] (все значения по умолчанию) */
{ "foo": "yes", "bar" : false }
  -> stdClass { $foo => 'yes', $bar => false }

{ "foo": "no", "array" : [ 5, 6 ] }
  -> stdClass { $foo => 'no', $array => [ 5, 6 ] }

{ "foo": "no", "obj" : { "embedded" : 3.14 } }
  -> stdClass { $foo => 'no', $obj => stdClass { $embedded => 3.14 } }

{ "foo": "yes", "__pclass": "MyClass" }
  -> stdClass { $foo => 'yes', $__pclass => 'MyClass' }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "MyClass" } }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x80, 'MyClass') }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "YourClass") }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x80, 'YourClass') }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "OurClass") }
  -> OurClass { $foo => 'yes', $__pclass => Binary(0x80, 'OurClass'), $unserialized => true }

{ "foo": "yes", "__pclass": { "$type" : "44", "$binary" : "YourClass") }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x44, 'YourClass') }

/* typemap: [ "root" => "MissingClass" ] */
{ "foo": "yes" }
  -> MongoDB\Driver\Exception\InvalidArgumentException("MissingClass does not exist")

/* typemap: [ "root" => "MyClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> MongoDB\Driver\Exception\InvalidArgumentException("MyClass does not implement Unserializable interface")

/* typemap: [ "root" => "MongoDB\BSON\Unserializable" ] */
{ "foo": "yes" }
  -> MongoDB\Driver\Exception\InvalidArgumentException("Unserializable is not a concrete class")

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MongoDB\BSON\Unserializable" } }
  -> YourClass { $foo => "yes", $__pclass => Binary(0x80, "MongoDB\BSON\Unserializable"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> YourClass { $foo => "yes", $__pclass => Binary(0x80, "MyClass"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "OurClass" } }
  -> OurClass { $foo => "yes", $__pclass => Binary(0x80, "OurClass"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "TheirClass" } }
  -> TheirClass { $foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true }

/* typemap: [ "root" => "OurClass" ] */
{ foo: "yes", "__pclass" : { "$type": "80", "$binary": "TheirClass" } }
  -> TheirClass { $foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true }

/* typemap: [ 'root' => 'YourClass' ] */
{ foo: "yes", "__pclass" : { "$type": "80", "$binary": "YourClass" } }
  -> YourClass { $foo => 'yes', $__pclass => Binary(0x80, 'YourClass'), $unserialized => true }

/* typemap: [ 'root' => 'array', 'document' => 'array' ] */
{ "foo": "yes", "bar" : false }
  -> [ "foo" => "yes", "bar" => false ]

{ "foo": "no", "array" : [ 5, 6 ] }
  -> [ "foo" => "no", "array" => [ 5, 6 ] ]

{ "foo": "no", "obj" : { "embedded" : 3.14 } }
  -> [ "foo" => "no", "obj" => [ "embedded => 3.14 ] ]

{ "foo": "yes", "__pclass": "MyClass" }
  -> [ "foo" => "yes", "__pclass" => "MyClass" ]

{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> [ "foo" => "yes", "__pclass" => Binary(0x80, "MyClass") ]

{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "OurClass" } }
  -> [ "foo" => "yes", "__pclass" => Binary(0x80, "OurClass") ]

/* typemap: [ 'root' => 'object', 'document' => 'object' ] */
{ "foo": "yes", "__pclass": { "$type": "80", "$binary": "MyClass" } }
  -> stdClass { $foo => "yes", "__pclass" => Binary(0x80, "MyClass") }

add a note add a note

User Contributed Notes 3 notes

up
2
Miguel
6 years ago
Make sure you include the field "__pclass" to the projection if you want the ODM to automatically call the bsonUnserialize of the class.

If you don't get that field in the query, the ODM will never know which class to call, so you'll have to specify it with the "typemap" variable.
up
1
hello at stefandjokic dot com
4 years ago
Minor correction to my previous comment:

Use 'fieldPaths' for specifying type of nested property.

Eg.

'typeMap' => [
                'array' => 'array',
                'fieldPaths' => [
                    'notifications.email.to' => 'array',
                    'notifications.data.params' => 'array',
                ],
            ],
up
0
hello at stefandjokic dot com
4 years ago
You can also specify the type map for nested document item, e.g.

<?php
.
.
.
// everything else is by default an object
'typeMap' => [
               
'customer.phones' => 'array',
            ],
To Top