XML XML Deserializa eserialization tion $data = <<<DATA <?xml version="1.0"?> <response> <id/> <title>Seven</title> <slug>seven</slug> <description>A … thriller!</description> <duration>130</duration> <releaseDate>1996-01-31</releaseDate> <storageKey/> </response> DATA; $movie = $serializer->deserialize($data, 'Movie', 'xml'); print_r($movie);
String String Deserializa eserialization tion Movie Object ( [id:Movie:private] => [title:Movie:private] => Seven [slug:Movie:private] => seven [description:Movie:private] => A … thriller! [duration:Movie:private] => 130 [releaseDate:Movie:private] => 1996-01-31 [storageKey:Movie:private] => )
Construc onstructor or Initializa Initialization tion class Movie class Movie { // ... function function __construct($id = null null, $title = null null, $slug = null null) { $this->id = $id; $this->title = $title; $this->slug = $slug; } } Constructor arguments must match properties names. ¡
Going Further with the Serializer
Getter, Hasser & Isser Methods Normalizer
// Setup the normalizers $normalizers normalizers[] = [] = new new Normalizer Normalizer\ObjectNormalizer ObjectNormalizer(); (); $normalizers normalizers[] = [] = new new Normalizer Normalizer\GetSetMethodNormalizer GetSetMethodNormalizer(); (); $normalizers[] = new new Normalizer\PropertyNormalizer(); // Setup the encoders $encoders[] = new new Encoder\JsonEncoder(); $encoders[] = new new Encoder\XmlEncoder(); // Setup the serializer $serializer = new new Serializer($normalizers, $encoders); // Use the serializer $serializer->serialize($object, 'json'); $serializer->deserialize($data, 'Acme\User','json'); The object normalizer can invoke « hasser » methods. ¡
class class Movie Movie { The normalizer invokes public public function function getId() { getter & isser methods. ¡ return return $this->id; } public public function function getTitle() { return return $this->title; } public public function function hasGenre() { return return false; } // ... public public function function isReleased() { return new return new \DateTime DateTime($this->releaseDate) <= new new \DateTime DateTime(); } }
JSON JSON Serializa erialization tion { "id":null, "title":"Seven", "slug":"seven", "description":"A … thriller!", "duration":130, "releaseDate":"1996-01-31", "storageKey":null, "genre" genre":false :false, , " "released released":true true, , }
XML XML Serializa erialization tion <?xml version="1.0"?> <response> <id/> <title>Seven</title> <slug>seven</slug> <description>A … thriller!</description> <duration>130</duration> <releaseDate>1996-01-31</releaseDate> <storageKey/> <genre>0</genre> <genre>0</genre> < <released released>1</ >1</released released> > </response>
Ignoring Attributes
$normalizer = new new GetSetMethodNormalizer(); $normalizer->setIgnoredAttributes([ 'storageKey' ]); <?xml version="1.0"?> <response> <id/> <title>Seven</title> <slug>seven</slug> <description>A … thriller!</description> <duration>130</duration> <releaseDate>1996-01-31</releaseDate> <released>1</released> </response>
Converting properties names to underscore case.
$converter = new new CamelCaseToSnakeCaseNameConverter(); $normalizer = new new GetSetMethodNormalizer(null, $converter); <?xml version="1.0"?> <response> <id/> <title>Seven</title> <slug>seven</slug> <description>A … thriller!</description> <duration>130</duration> <release_date release_date>1996-01-31</ >1996-01-31</release_date release_date> > <released>1</released> </response>
Customizing all serialized properties names.
class class PrefixNameConverter PrefixNameConverter implements implements NameConverterInterface { private private $prefix; public public function function __construct($prefix) { $this->prefix = $prefix; } public public function function normalize($propertyName) { return return $this->prefix.'_'.$propertyName; } public public function function denormalize($propertyName) { if if ($this->prefix.'_' === substr($propertyName, 0, count($this->prefix))) { return return substr($propertyName, count($this->prefix)); } return return $propertyName; } } The NameConverterInterface has been introduced in 2.7. ¡
$converter = new new PrefixNameConverter('movie'); $normalizer = new new GetSetMethodNormalizer(null, $converter); <?xml version="1.0"?> <response> <movie_id/> <movie_title>Seven</movie_title> <movie_slug>seven</movie_slug> <movie_description>A … thriller!</movie_description> <movie_duration>130</movie_duration> <movie_release_date>1996-01-31</movie_release_date> <movie_released>1</movie_released> </response>
Changing the XML root name.
$serializer->serialize($movie, 'xml', [ 'xml_root_node_name' => 'movie', ]); <?xml version="1.0"?> <movie> <id/> <title>Seven</title> ... </movie>
Deserializing into an existing object.
$data = <<<DATA <?xml version="1.0"?> <movie> <duration>130</duration> <releaseDate>1996-01-31</releaseDate> </movie> DATA; $movie1 = new new Movie(1234, 'Seven', 'seven'); $movie2 = $serializer->deserialize($data, 'Movie', 'xml', [ 'xml_root_node_name' => 'movie', 'object_to_populate' => $movie1, ]);
Movie Object ( [id:Movie:private id:Movie:private] => 1234 ] => 1234 [ [title:Movie:private title:Movie:private] => ] => Seven Seven [ [slug:Movie:private slug:Movie:private] => ] => seven seven [description:Movie:private] => [duration:Movie:private duration:Movie:private] => 130 ] => 130 [ [releaseDate:Movie:private releaseDate:Movie:private] => 1996-01-31 ] => 1996-01-31 [storageKey:Movie:private] => [genre:Movie:private] => ) The « description » property remains empty while « duration » and « releaseDate » properties are set. ¡
Serializer Advanced Features
Serializing More Complex Object Graphs.
class class Movie Movie { /** @var Genre */ private private $genre; /** @var Directors[] */ private private $directors; /** * Each role keeps a reference to that Movie object * and a reference to an Actor object playing that * role in the movie. * * @var Role[] */ private private $roles; }
One to One Unidirectional Relationship
$genre = new new Genre(42, 'Thriller', 'thriller'); $movie = new new Movie(1234, 'Seven', 'seven'); $movie->setGenre($genre); $movie->setStorageKey('movie-42-1234'); $movie->setDuration(130); $movie->setDescription('A brilliant thriller!'); $movie->setReleaseDate('1996-01-31'); echo echo $serializer->serialize($movie, 'xml', [ 'xml_root_node_name' => 'movie', ]);
<?xml version="1.0"?> <movie movie> > <genre> <genre> <id> <id>42</id> </id> <slug> <slug>thriller</slug> </slug> <title < title>Thriller</ </title title> > </genre> </genre> <id> <id>1234</id> </id> < <title title>Seven</ </title title> > <duration> <duration>130</duration> </duration> < <released released>1</ </released released> > <slug> <slug>seven</slug> </slug> <description> <description>A brilliant thriller!</description> </description> < <release_date release_date>1996-01-31</ </release_date release_date> > </ </movie movie>
{ "genre":{ "id":42, "slug":"thriller", "title":"Thriller" }, "id":1234, "title":"Seven", "duration":130, "released":true, "slug":"seven", "description":"A brilliant thriller!", "release_date":"1996-01-31" }
One to Many Unidirectional Relationship
$fincher = new new Director(); $fincher->setId(973463); $fincher->setName('David Fincher'); $fincher->setBirthday('1962-05-10'); $kopelson = new new Director(); $kopelson->setId(783237); $kopelson->setName('Arnold Kopelson'); $kopelson->setBirthday('1935-02-14'); $movie = new new Movie(1234, 'Seven', 'seven'); $movie->addDirector($fincher); $movie->addDirector($kopelson);
<?xml version="1.0"?> <movie movie> > <!-- ... --> <!-- ... --> < <directors directors> > <id> <id>973463</id> </id> < <name name>David Fincher</ </name name> > < <birthday birthday>1962-05-10</ </birthday birthday> > < <deathday deathday/> /> </ </directors directors> > < <directors directors> > <id> <id>783237</id> </id> < <name name>Arnold Kopelson</ </name name> > <birthday < birthday>1935-02-14</ </birthday birthday> > < <deathday deathday/> /> </ </directors directors> > </ </movie movie>
{ "genre":{ "id":42, "slug":"thriller", "title":"Thriller" }, "id":1234, "title":"Seven", "duration":130, "released":true, "slug":"seven", "description":"A brilliant thriller!", "release_date":"1996-01-31", "directors":[ { "id":973463, "name":"David Fincher", "birthday":"1962-05-10", "deathday":null }, { "id":783237, "name":"Arnold Kopelson", "birthday":"1935-02-14", "deathday":null } ] }
Many to Many Bidirectional Relationship
class class Role Role { private private $id; private private $character; private private $movie; private private $actor; function function __construct($id, Movie $movie, Actor $actor, $character) { $this->id = $id; $this->movie = $movie; $this->actor = $actor; $this->character = $character; } } The « Role » instance keeps a reference to the « Movie » that also keeps references to « roles » played by actors. ¡
$movie = new new Movie(1234, 'Seven', 'seven'); // ... $pitt = new new Actor(); $pitt->setId(328470); $pitt->setName('Brad Pitt'); $pitt->setBirthday('1963-12-18'); $freeman = new new Actor(); $freeman->setId(329443); $freeman->setName('Morgan Freeman'); $freeman->setBirthday('1937-06-01'); $mills = new new Role(233, $movie, $pitt, 'David Mills'); $sommerset = new new Role(328, $movie, $freeman, 'William Sommerset'); $movie->addRole($mills); $movie->addRole($sommerset); $serializer->serialize($movie, 'json');
PHP Fatal PHP Fatal error error: Uncaught exception 'Symfony\Component\Serializer \Exception \CircularReferenceException' with message 'A circular reference has been detected (configured limit: 1).' in /Volumes/Development/Sites/ Serializer/vendor/symfony/serializer/ Normalizer/AbstractNormalizer.php:221
Handling Circular References
Handling Handling Cir Circular ular Ref Refer erenc ences es $normalizer = new new ObjectNormalizer(null null, $converter); $normalizer->setIgnoredAttributes([ 'storageKey' ]); // Return the object unique identifier instead of the // instance to stop a potential infinite serialization loop. $normalizer->setCircularReferenceHandler(function function ($object) { return return $object->getId(); }); Circular references support has been introduced in Symfony 2.6. ¡
{ ... "roles":[ { "actor":{ "id":328470, "name":"Brad Pitt", "birthday":"1963-12-18", "deathday":null }, "character":"David Mills", "id":233163, "movie":1234 "movie":1234 }, ... ] }
Using Callback Normalizers.
Actors, Directors and Movies now stores date representations as « DateTime » objects. These instance must be serialized too. ¡ $movie = new new Movie(1234, 'Seven', 'seven'); $movie->setReleaseDate(new \DateTime('1996-01-31')); $pitt = new new Actor(); $pitt->setBirthday(new \DateTime('1963-12-18')); $fincher = new new Director(); $fincher->setBirthday(new \DateTime('1962-05-10')); $serializer->serialize($movie, 'json');
Without custom serializer to handle « DateTime » instance, the Serializer serializes any date object as follows: ¡ <release_date release_date> > <last_errors < last_errors> > < <warning_count warning_count>0</ </warning_count warning_count> > <warnings/> <warnings/> < <error_count error_count>0</ </error_count error_count> > <errors < errors/> /> </last_errors </ last_errors> > < <timezone timezone> > < <name name>Europe/Paris</ </name name> > <location> <location> <country_code < country_code>FR</ </country_code country_code> > <latitude> <latitude>48.86666</latitude> </latitude> <longitude> <longitude>2.33333</longitude> </longitude> < <comments comments></ ></comments comments> > </location> </location> </ </timezone timezone> > <offset> <offset>3600</offset> </offset> < <timestamp timestamp>823042800</ </timestamp timestamp> > </ </release_date release_date>
The built-in normalizers allow to set PHP callbacks to handle custom serialization steps. ¡ $normalizer = new new Normalizer\ObjectNormalizer(... ...); $callback = function function ($dateTime) { return return $dateTime instanceof \DateTime ? $dateTime->format(\DateTime::ISO8601) : ''; }; $normalizer->setCallbacks([ 'releaseDate' => $callback, 'birthday' => $callback, 'deathday' => $callback, ]);
<?xml version="1.0"?> <movie movie> > <!-- ... --> <!-- ... --> <release_date < release_date>1996-01-31T00:00:00+0100</ </release_date release_date> > < <directors directors> > <id> <id>973463</id> </id> < <name name>David Fincher</ </name name> > < <birthday birthday>1962-05-10T00:00:00+0100</ </birthday birthday> > < <deathday deathday/> /> </ </directors directors> > < <directors directors> > <id> <id>783237</id> </id> < <name name>Arnold Kopelson</ </name name> > < <birthday birthday>1935-02-14T00:00:00+0000</ </birthday birthday> > < <deathday deathday/> /> </ </directors directors> > </ </movie movie>
{ "genre":{ "id":42, "slug":"thriller", "title":"Thriller" }, "id":1234, "title":"Seven", "duration":130, "released":true, "slug":"seven", "description":"A brilliant thriller!", "release_date":"1996-01-31T00:00:00+0000", "release_date":"1996-01-31T00:00:00+0000", "directors":[ { "id":973463, "name":"David Fincher", "birthday":"1962-05-10T00:00:00+0000", "birthday":"1962-05-10T00:00:00+0000", "deathday":null }, { "id":783237, "name":"Arnold Kopelson", "birthday":"1935-02-14T00:00:00+0000", "birthday":"1935-02-14T00:00:00+0000", "deathday":null } ] }
Using the Custom Normalizer.
Adding dding the C the Cust ustom om Normaliz ormalizer er The built-in « Custom » normalizer is responsible for automatically calling the « normalize() » and « denormalize() » methods of your objects if they implement the corresponding interface. ¡ $normalizers[] = new new Normalizer\CustomNormalizer();
use use Symfony\Component\Serializer\Normalizer\NormalizableInterface; use use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class class Role Role implements implements NormalizableInterface { private private $id; private private $character; private private $movie; private private $actor; function function normalize(NormalizerInterface $normalizer, $format = null null, array array $context = []) { return return [ 'id' => $this->id, 'character' => $this->character, 'actor' => $this->actor, ]; } }
Serialization Groups
Annota Annotation C tion Configura onfiguration tion use use Symfony\Component\Serializer\Annotation\Groups; class Movie class Movie { /** @Groups({"admins"}) */ private private $id; /** @Groups({"admins", "publishers", "users" }) */ private private $title; /** @Groups({"admins", "publishers" }) */ private private $slug; /** @Groups({"admins", "publishers", "users" }) */ private private $releaseDate; /** @Groups({ "admins", "publishers", "users" }) */ public public function function isReleased() { return return new new $this->releaseDate <= new new \DateTime(); } }
YAML C ML Configura onfiguration tion Movie: attributes: id: groups: [ admins ] title: groups: [ admins, publishers, users ] slug: groups: [ admins, publishers ] releaseDate: groups: [ admins, publishers, users ] released: groups: [ admins, publishers, users ]
XML C XML Configura onfiguration tion <?xml version="1.0" ?> <serializer serializer ...> <class <class name="Movie"> <attribute <attribute name="id"> <group> <group>admins</group> </group> </ </attribute attribute> <attribute attribute name="title"> <group> <group>admins</group> </group> <group> <group>publishers</group> </group> <group> <group>users</group> </group> </ </attribute attribute> <attribute attribute name="slug"> <group> <group>admins</group> </group> <group> <group>publishers</group> </group> </ </attribute attribute> </class> </class> </ </serializer serializer>
Load oad Gr Groups oups Metada Metadata ta use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use use use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use use use Doctrine\Common\Annotations\AnnotationReader; use use Doctrine\Common\Cache\ArrayCache; // Setup a loader $loader = new new AnnotationLoader(new new AnnotationReader()); $loader = new new YamlFileLoader(__DIR__.'/config/serializer.yml'); $loader = new new XmlFileLoader(__DIR__.'/config/serializer.xml'); $cache = new new ArrayCache(); // Setup the normalizers $factory = new new ClassMetadataFactory($loader, $cache); $normalizer = new new Normalizer\ObjectNormalizer($factory, $converter); // ...
Serializa erialization tion Gr Groups oups $serializer->serialize($movie, 'xml', [ 'xml_root_node_name' => 'movie', 'groups' => [ 'users' ], ]); $serializer->deserialize($movie, 'Movie', 'xml', [ 'xml_root_node_name' => 'movie', 'groups' => [ 'users' ], ]);
Serializer Integration into Drupal 8
The « Serialization » Core module integrates the Symfony Serializer into Drupal. »
core core/modules/ /modules/serialization serialization/ / ├── serialization.info.yml ├── serialization.module ├── serialization.services.yml serialization.services.yml ├── src/ │ ├── Encoder/ Encoder/ │ ├── EntityResolver EntityResolver/ / │ ├── Normalizer Normalizer/ / │ ├── RegisterEntityResolversCompilerPass.php │ ├── RegisterSerializationClassesCompilerPass.php │ ├── SerializationServiceProvider.php │ └── Tests/ └── tests/ ├── modules/ ├── serialization_test/ └── src/
Built Built-in -in Normaliz ormalizers ers core/modules/serialization/src/Normalizer/ ├─ ComplexDataNormalizer.php (default) (default) ├─ ConfigEntityNormalizer.php ├─ ContentEntityNormalizer.php ├─ EntityNormalizer.php ├─ ListNormalizer.php ├─ NormalizerBase.php ├─ NullNormalizer.php └─ TypedDataNormalizer.php
Recommend
More recommend