Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony Serializer Component

Avatar for jucycabrera jucycabrera
November 22, 2019

Symfony Serializer Component

Slides presented in SymfonyCon Amsterdam

Avatar for jucycabrera

jucycabrera

November 22, 2019
Tweet

More Decks by jucycabrera

Other Decks in Technology

Transcript

  1. Juciellen Cabrera @jucycabrera Brazilian Mother Developer over 10 years Software

    Engineer | Smartbox in Dublin Coorganizer | PHPWomen Brazil 2
  2. Motivation 3 • json_encode() doesn’t do everything we need •

    unserialize() is not safe • Different views for the same object • Recursive encoding/decoding
  3. Serializer Component 4 Turns objects into a specific format (XML,

    JSON, Yaml, ...) and the other way around.
  4. 5

  5. Serialization is The process of converting an object into a

    stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/serialization/ 7
  6. Converting object into array • ObjectNormalizer • DateTimeZoneNormalizer • PropertyNormalizer

    • GetSetMethodNormalizer • DataUriNormalizer • ConstraintViolationListNormalizer • DateIntervalNormalizer • JsonSerializableNormalizer • CustomNormalizer • ArrayDenormalizer Normalizers 10
  7. PropertyNormalizer • Directly reads and writes properties • Even being

    private or protected properties • It supports calling the constructor during the denormalization process.
  8. ObjectNormalizer The most powerful normalizer It is configured by default

    in Symfony applications with the Serializer component enabled. • Can access properties directly • Or getters, setters, hassers, adders and removers • Supports calling the constructor during the denormalization process. *PropertyAccess Component required 13
  9. Serializer class /** * @param (NormalizerInterface|DenormalizerInterface)[] $normalizers * @param (EncoderInterface|DecoderInterface)[]

    $encoders */ public function __construct(array $normalizers = [], array $encoders = []) 18
  10. You can define all normalizers and encoders that you want

    to be available $normalizers = [$datetimeNormalizer, propertyNormalizer]; $encoders = [$jsonEncoder, $csvEncoder, $xmlEncoder]; $serializer = new Serializer( $normalizers, $encoders ); Serializer class 19
  11. Serializer class - some methods • public function serialize($data, $format,

    array $context = []); • public function deserialize($data, $type, $format, array $context = []); • public function normalize($data, $format = null, array $context = []) • public function denormalize($data, $type, $format = null, array $context = []) • final public function encode($data, $format, array $context = []) • final public function decode($data, $format, array $context = []) 20
  12. class Pokemon { private $id; private $name; private $cp; private

    $caughtAt; private $weight; private $height; private $hp; private $battles; } 21
  13. To use the Serializer component, set up the Serializer specifying

    which encoders and normalizer are going to be available. echo $serializer->serialize($pokemon,JsonEncoder::FORMAT); { "id": 25, "name": "Pikachu", "cp": 450, "caughtAt": "2019-11-17T21:24:03+00:00", "weight": 6, "height": 0.4, "hp": 66, "battles": null } Serializing to JSON 23
  14. Selecting attributtes $context = [ ObjectNormalizer::ATTRIBUTES => [ 'id','name', 'cp'

    ] ]; echo $serializer->serialize($pokemon, 'json', $context); { "id": 25, "name": "Pikachu", "cp": 450 } 24
  15. Ignoring Attributes $context = [ AbstractObjectNormalizer::IGNORED_ATTRIBUTES => [ 'battles', 'caughtAt']

    ]; echo $serializer->serialize($player, 'json', $context); { "id": 25, "name": "Pikachu", "cp": 450, "weight": 6, "height": 0.4, "hp": 66 } 25
  16. Skipping null values $pokemon = new Pokemon(); $pokemon->setId(25); $pokemon->setName('Pikachu'); $pokemon->setHp(527);

    $context = [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]; echo $serializer->serialize($pokemon,'json', $context); { "pokemonId": 25, "name": "Pikachu", "hp": 527 } 26
  17. Boolean Attributes Methods prefixed by is, the Serializer component will

    automatically detect and use it to serialize related attributes. class Pokemon { public function isAlive() { return (boolean) $this->hp; } } { "id": 25, "name": "Pikachu", "cp": 450, "caughtAt": "2019-11-18T23:03:38+00:00", "weight": 6, "height": 0.4, "hp": 66, "battles": null, "alive": true } 28
  18. SerializedName echo $serializer->serialize($pokemon,'json', $context); { "pokemonId": 25, "name": "Pikachu", "cp":

    450, "caughtAt": "2019-11-18T23:03:38+00:00", "weight": 6, "height": 0.4, "hp": 66, "battles": null } 30
  19. Groups XML, YAML and PHP supported Symfony\Component\Serializer\Annotation\Group s; class Pokemon

    { /** * @var int * @Groups("list") */ public $id; /** * @var string * @Groups({"list","catch"}) */ private $name; /** * @var integer * @Groups({"list","catch"}) */ private $cp; /** * @var \DateTime * @Groups({list") */ private $caughtAt; /** * @var float * @Groups("list") */ private $weight; /** * @var float * @Groups("list") */ private $height; 31
  20. Groups $context = [ObjectNormalizer::GROUPS=>[list]]; echo $serializer->serialize($pokemon, 'json', $context); { "id":

    25, "name": "Pikachu", "cp": 450, "caughtAt": "2019-11-18T23:26:13+00:00", "weight": 6, "height": 0.4 } 33
  21. class Pokemon { /** * @var Battle [] */ private

    $battles; } class Battle { /** * @var Pokemon [] */ protected $participants; } Circular Reference Circular references are common when dealing with entity relations: 34
  22. To avoid infinite loops, GetSetMethodNormalizer or ObjectNormalizer throw a CircularReferenceException

    when such a case is encountered: echo $serializer->serialize($battle, 'json'); // A circular reference has been detected when serializing the object of class "Battle" (configured limit: 1) Circular Reference 35
  23. Instead of throwing an exception, circular references can also be

    handled by custom callables $defaultContext = [ AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) { return $object->getDate()->format(DateTime::RFC3339); }, ]; $objectNormalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext); Circular Reference 36
  24. echo $serializer->serialize($battle, 'json'); { "participants": [{ "name": "Ursaring", "battles": ["2019-11-11T22:19:38+00:00"]

    }, { "name": "Muk", "battles": ["2019-11-11T22:19:38+00:00"] }], "date": "2019-11-11T22:19:38+00:00" } Circular Reference 37
  25. Handling Arrays Deserialization $collection = $serializer->deserialize($serializedCollection, 'Pokemon[]', JsonEncoder::FORMAT ); var_dump($collection);

    array(2) { [0]=> object(Pokemon)#37 (8) { ["name":"Pokemon":private]=> string(7) "Porygon" } [1]=> object(Pokemon)#36 (8) { ["name":"Pokemon":private]=> string(5) "Ditto" } } 39
  26. Type Safety * PropertyInfo Component class Pokemon { /** *

    @var int */ private $id; /** * @var string */ private $name; /** * @var float */ private $weight; } 40
  27. Type Safety * PropertyInfo Component $json = '{"id":”52”,"name":"Meowth","weight":”1.5”}'; var_dump($serializer->deserialize($json, Pokemon::class,

    'json')); //The type of the "id" attribute for class "Pokemon" must be one of "int" ("string" given). 41
  28. Type Safety * PropertyInfo Component $json = '{"id":52,"name":"Meowth","weight":1.5}'; var_dump($serializer->deserialize($json, Pokemon::class,

    'json')); object(Pokemon)#13 (8) { ["id"]=> int(52) ["name":"Pokemon":private]=> string(6) "Meowth" ["weight":"Pokemon":private]=> float(1.5) } 42
  29. Recursivity $json = ' { "id": 52, "name": "Meowth", "weight":

    1.5, "caughtAt": "2019-10-23 11:35:00" }'; var_dump($serializer->deserialize($json, Pokemon::class, 'json')); 43
  30. Recursivity object(Pokemon)#14 (8) { ["id":"Pokemon":private]=> int(52) ["name":"Pokemon":private]=> string(6) "Meowth" ["cp":"Pokemon":private]=>

    NULL ["caughtAt":"Pokemon":private]=> object(DateTime)#15 (3) { ["date"]=> string(26) "2019-10-23 11:35:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } ["weight":"Pokemon":private]=> float(1.5) ["height":"Pokemon":private]=> NULL ["hp":"Pokemon":private]=> NULL ["battles":"Pokemon":private]=> NULL } 44
  31. XmlEncoder echo $serializer->serialize($collection, XmlEncoder::FORMAT); <?xml version="1.0" encoding="UTF-8"?> <response> <item key="0">

    <id>1</id> <name>Bulbasaur</name> <cp>250</cp> <caughtAt>2019-11-14T00:04:44+00:00</caughtAt> <weight>0.4</weight> <height>2.5</height> <xp>527</xp> <hp>527</hp> <alive>1</alive> </item> <item key="1"> <id>134</id> <name>Vaporeon</name> <cp>751</cp> <caughtAt>2019-11-14T00:04:44+00:00</caughtAt> <weight>3.09</weight> <height>0.84</height> <xp>112</xp> <hp>112</hp> <alive>1</alive> </item> </response> 47
  32. What's more? • Custom Normalizers • Handling Constructor Arguments •

    Serializing Interfaces and Abstract Classes • Depth limit 48