$30 off During Our Annual Pro Sale. View Details »

Introduction to Serialization with Symfony2

Hugo Hamon
November 23, 2012

Introduction to Serialization with Symfony2

Symfony comes with a Serializer component, which allows you to normalize and serialize your objects to multiple representations such as XML or JSON. This component becomes very helpful in certain circumstances like exposing data through a REST API. This talk will be split into three different parts. The first part will introduce the Serializer component underneath architecture and its basic usage. Then, you will learn how easy it is to extend the serializer in order to add custom serialization formats. Finally, you will discover how you can take benefit from the JMSSerializerBundle bundle that makes objects serialization easy, fun and intuitive with annotations.

Hugo Hamon

November 23, 2012
Tweet

More Decks by Hugo Hamon

Other Decks in Technology

Transcript

  1. http://www.flickr.com/photos/netzer/38270856
    Leveraging the
    Serializer Component

    View Slide

  2. Hugo
    HAMON
    SensioLabs

    View Slide

  3. 1.
    Introduction

    View Slide

  4. Serialization is the process of
    converting an object state
    into a format that can be
    stored and resurrected later
    Wikipedia  

    View Slide

  5. The Serializer
    serializes and
    unserializes objects.

    View Slide

  6. Normalizers
    Array
    Serializer
    Object
    XML
    JSON
    YAML

    Denormalizers
    Encoders
    Decoders

    View Slide

  7. The Serializer
    Interface.

    View Slide

  8. namespace Symfony\Component\Serializer;
    interface SerializerInterface
    {
    /**
    * Serializes data in the appropriate format
    *
    * @param mixed $data any data
    * @param string $format format name
    * @return string
    */
    public function serialize($data, $format);
    /**
    * Deserializes data into the given type.
    *
    * @param mixed $data
    * @param string $type
    * @param string $format
    */
    public function deserialize($data, $type, $format);
    }

    View Slide

  9. The Normalizer
    Interface.

    View Slide

  10. namespace Symfony\Component\Serializer\Normalizer;
    interface NormalizerInterface
    {
    /**
    * Normalizes an object into a set of arrays/scalars
    *
    * @param object $object object to normalize
    * @param string $format format the normalization result will be encoded as
    * @return array|scalar
    */
    public function normalize($object, $format = null);
    /**
    * Checks whether the given data are supported for normalization.
    *
    * @param mixed $data Data to normalize.
    * @param string $format The format being (de-)serialized from or into.
    * @return Boolean
    */
    public function supportsNormalization($data, $format = null);
    }

    View Slide

  11. The Denormalizer
    Interface.

    View Slide

  12. interface DenormalizerInterface
    {
    /**
    * Denormalizes data back into an object of the given class.
    *
    * @param mixed $data data to restore
    * @param string $class the expected class to instantiate
    * @param string $format format the given data was extracted from
    * @return object
    */
    public function denormalize($data, $class, $format = null);
    /**
    * Checks whether the given data are supported for denormalization.
    *
    * @param mixed $data Data to denormalize from.
    * @param string $type The class to which the data should be denormalized.
    * @param string $format The format being deserialized from.
    * @return Boolean
    */
    public function supportsDenormalization($data, $type, $format = null);
    }

    View Slide

  13. The Encoder
    Interface.

    View Slide

  14. namespace Symfony\Component\Serializer\Encoder;
    interface EncoderInterface
    {
    /**
    * Encodes data into the given format
    *
    * @param mixed $data Data to encode
    * @param string $format Format name
    *
    * @return scalar
    */
    public function encode($data, $format);
    /**
    * Checks whether the serializer can encode to given format
    *
    * @param string $format format name
    * @return Boolean
    */
    public function supportsEncoding($format);
    }

    View Slide

  15. The Decoder
    Interface.

    View Slide

  16. namespace Symfony\Component\Serializer\Encoder;
    interface DecoderInterface
    {
    /**
    * Decodes a string into PHP data
    *
    * @param string $data Data to decode
    * @param string $format Format name
    *
    * @return mixed
    */
    public function decode($data, $format);
    /**
    * Checks whether the serializer can decode from given format
    *
    * @param string $format format name
    * @return Boolean
    */
    public function supportsDecoding($format);
    }

    View Slide

  17. 2.
    Basic Usage

    View Slide

  18. use Symfony\Component\Serializer\Serializer;
    use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\Serializer\Encoder\XmlEncoder;
    // Register the normalizers
    $normalizers[] = new GetSetMethodNormalizer();
    // Register the encoders
    $encoders[] = new JsonEncoder();
    $encoders[] = new XmlEncoder();
    // Create and initialize the serializer
    $serializer = new Serializer($normalizers, $encoders);

    View Slide

  19. Serializing an
    Object.

    View Slide

  20. $serializer
    ->serialize($object, 'xml')
    ;

    View Slide

  21. View Slide

  22. Ignoring
    attributes from
    serialization.

    View Slide

  23. $ignore = array('id', 'customer');
    $normalizer = new GetSetMethodNormalizer();
    $normalizer->setIgnoredAttributes($ignore);

    View Slide

  24. No more ID and Customer name!

    View Slide

  25. Unserializing
    a string.

    View Slide

  26. $xml = '...';
    // Convert an XML string to an array
    $data = $serializer->decode($xml, 'xml');
    // Convert the array to an object
    $class = 'Acme\BookingBundle\Booking';
    $booking = $serializer
    ->denormalize($data, $class, 'xml')
    ;

    View Slide

  27. 3.
    Extending the
    Serializer

    View Slide

  28. Adding support
    for YAML
    serialization.

    View Slide

  29. namespace Acme\SerializerBundle\Encoder;
    use Symfony\Component\Serializer\Encoder\EncoderInterface;
    use Symfony\Component\Yaml\Dumper;
    class YamlEncoder implements EncoderInterface
    {
    public function encode($data, $format)
    {
    $yaml = new Dumper();
    return $yaml->dump($data, 2);
    }
    public function supportsEncoding($format)
    {
    return 'yaml' === $format;
    }
    }

    View Slide

  30. reference: SLT-123456
    arrival: '2012-09-26'
    departure: '2012-09-29'
    designation: 'Deluxe Suite'

    View Slide

  31. Adding support
    for YAML
    unserialization.

    View Slide

  32. namespace Acme\SerializerBundle\Encoder;
    // ...
    use Symfony\Component\Serializer\Encoder\DecoderInterface;
    use Symfony\Component\Yaml\Parser;
    class YamlEncoder implements EncoderInterface, DecoderInterface
    {
    // ...
    public function decode($data, $format)
    {
    $yaml = new Parser();
    return $yaml->parse($data);
    }
    public function supportsDecoding($format)
    {
    return 'yaml' === $format;
    }
    }

    View Slide

  33. $yaml = <<reference: SLT-123456
    arrival: '2012-09-26'
    departure: '2012-09-29'
    designation: 'Deluxe Suite'
    EOY;
    $class = 'Acme\BookingBundle\Booking';
    $data = $serializer->decode($yaml, 'yaml');
    $booking = $serializer->denormalize($data, $class, 'yaml');
    echo $booking->getReference();

    View Slide

  34. 4.
    The JMSSerializer
    Bundle

    View Slide

  35. The Serializer
    service.

    View Slide

  36. $s = $container->get('serializer');
    $json = $s->serialize($object, 'json');
    $xml = $s->serialize($object, 'xml');
    $yml = $s->serialize($object, 'yml');
    $type = 'Acme\BookingBundle\Booking';
    $obj = $s->deserialize($xml, $type, 'xml');
    $obj = $s->deserialize($yml, $type, 'yml');

    View Slide

  37. Twig helpers.

    View Slide

  38. {# uses JSON #}
    {{ booking | serialize }}
    {# Custom output format #}
    {{ booking | serialize('json') }}
    {{ booking | serialize('xml') }}
    {{ booking | serialize('yml') }}

    View Slide

  39. YAML
    Con guration

    View Slide

  40. # AcmeBookingBundle\Resources\config\serializer\Booking.yml
    Acme\BookingBundle\Booking:
    exclusion_policy: ALL
    xml_root_name: booking
    exclude: true
    access_type: public_method
    # ...
    properties:
    reference:
    expose: true
    type: string
    amount:
    expose: true
    type: double
    # ...

    View Slide

  41. XML
    Con guration

    View Slide

  42. # AcmeBookingBundle\Resources\config\serializer\Booking.xml


    name="Acme\BookingBundle\Booking"
    exclusion-policy="ALL"
    xml-root-name="booking"
    access-type="public_method"
    >




    View Slide

  43. Annotations
    Con guration

    View Slide

  44. namespace Acme\BookingBundle;
    use JMS\SerializerBundle\Annotation as Serialize;
    /**
    * @Serialize\XmlRoot("booking")
    */
    class Booking
    {
    /**
    * @Serialize\XmlAttribute
    * @Serialize\Type("string")
    */
    private $reference;
    // ...
    }

    View Slide

  45. Customizing
    the XML root
    node.

    View Slide

  46. namespace Acme\BookingBundle;
    use JMS\SerializerBundle\Annotation as Serialize;
    /**
    * @Serialize\XmlRoot("booking")
    */
    class Booking
    {
    // ...
    }

    View Slide



  47. SLT-123456
    2012-09-26
    2012-09-29
    Deluxe Suite

    View Slide

  48. Serializing a
    property to a
    XML attribute.

    View Slide

  49. namespace Acme\BookingBundle;
    use JMS\SerializerBundle\Annotation as Serialize;
    /**
    * @Serialize\XmlRoot("booking")
    */
    class Booking
    {
    /** @Serialize\XmlAttribute */
    private $reference;
    // ...
    }

    View Slide



  50. 2012-09-26
    2012-09-29
    Deluxe Suite

    View Slide

  51. Serializing an
    object to a
    single XML node

    View Slide

  52. /**
    * @Serialize\XmlRoot("booking")
    *
    */
    class Booking
    {
    /**
    * @var Price
    */
    private $price;
    }

    View Slide

  53. class Price
    {
    /** @Serialize\XmlValue */
    private $amount;
    /** @Serialize\XmlAttribute */
    private $vat;
    /** @Serialize\XmlAttribute */
    private $currency;
    }

    View Slide

  54. $booking = new Booking();
    $booking->setReference('SLT-123456');
    // ...
    $booking->setPrice(new Price(120, 12, 'EUR'));



    2012-09-29
    120

    View Slide

  55. Excluding or
    Exposing
    properties.

    View Slide

  56. /**
    * @Serialize\ExclusionPolicy("all")
    * @Serialize\XmlRoot("booking")
    */
    class Booking
    {
    /**
    * @Serialize\Expose
    */
    private $reference;
    }

    View Slide


  57. SLT-123456

    View Slide

  58. /**
    * @Serialize\ExclusionPolicy("none")
    * @Serialize\XmlRoot("booking")
    */
    class Booking
    {
    /**
    * @Serialize\Exclude
    */
    private $price;
    }

    View Slide



  59. SLT-123456
    2012-09-26
    2012-09-29
    Deluxe Suite

    View Slide

  60. Managing objects
    properties
    versioning

    View Slide

  61. // ...
    class Booking
    {
    /**
    * @Serialize\Since("1.8.4")
    * @Serialize\SerializedName("loyalty")
    */
    private $loyaltyNumber;
    // ...
    }

    View Slide

  62. // ...
    class Booking
    {
    /**
    * @Serialize\Until("1.8.3")
    * @Serialize\SerializedName("loyalty")
    */
    private $membershipNumber;
    // ...
    }

    View Slide

  63. $booking->setMembershipNumber('3942945839');
    $booking->setLoyaltyNumber('0973525332');
    $serializer->setVersion('1.8.2');



    3942945839

    View Slide

  64. $booking->setMembershipNumber('3942945839');
    $booking->setLoyaltyNumber('0973525332');
    $serializer->setVersion('1.9');



    0973525332

    View Slide

  65. Detecting the
    version & format
    automatically

    View Slide

  66. namespace Acme\BookingBundle\Listener;
    use Symfony\Component\HttpKernel\Event\KernelEvent;
    use JMS\SerializerBundle\Serializer\SerializerInterface;
    class ApiVersionListener
    {
    public function __construct(SerializerInterface $serializer)
    {
    $this->serializer = $serializer;
    }
    public function onKernelRequest(KernelEvent $event)
    {
    $request = $event->getRequest();
    $accept = $request->headers->get('Accept');
    // look for: application/vnd.hhamon-v{version}+{format}
    if ($accept && preg_match('/([0-9\.]+)?\+(xml|json)$/', $accept, $matches)) {
    $request->setRequestFormat($matches[2]);
    $this->serializer->setVersion($matches[1]);
    }
    }
    }

    View Slide

  67. Offering several
    views of a
    serialized object.

    View Slide

  68. // ...
    class Booking
    {
    /** @Serialize\Groups({ "list" }) */
    private $id;
    /** @Serialize\Groups({ "list", "details" }) */
    private $reference;
    /** @Serialize\Groups({ "details" }) */
    private $designation;
    }

    View Slide


  69. SLT-123456
    Deluxe Suite

    $serializer = $this->get('serializer');
    $serializer->setGroups(array('details'));
    $serializer->serialize($booking, 'xml');

    View Slide

  70. Forcing the
    serializer to call
    getters & setters.

    View Slide

  71. class Booking
    {
    /** @Serialize\AccessType("public_method") */
    private $arrival;
    public function setArrival($arrival)
    {
    $this->arrival = new \DateTime($arrival);
    }
    public function getArrival()
    {
    return $this->arrival->format('m/d/Y');
    }
    }

    View Slide



  72. SLT-123456
    09/26/2012
    2012-09-29


    View Slide

  73. Mapping a property
    to another getter or
    setter method.

    View Slide

  74. class Booking
    {
    /**
    * @Serialize\Accessor(
    * setter = "setShortDesignation",
    * getter = "getShortDesignation"
    * )
    *
    */
    private $designation;
    }

    View Slide

  75. Customizing the
    order in which
    properties are
    serialized.

    View Slide

  76. /**
    * @Serialize\AccessOrder("alphabetical")
    *
    */
    class Booking
    {
    // ...
    }

    View Slide

  77. /**
    * @Serialize\AccessOrder(custom = {
    * "reference",
    * "arrival",
    * "departure"
    * })
    *
    */
    class Booking
    {
    // ...
    }

    View Slide



  78. SLT-123456
    2012-09-26
    2012-09-29
    Deluxe Suite
    3942945839
    119.6

    View Slide

  79. Typing properties
    for a complete
    unserialization.

    View Slide

  80. class Booking
    {
    /** @Serialize\Type("string") */
    private $reference;
    /** @Serialize\Type("DateTime") */
    private $arrival;
    /** @Serialize\Type("Acme\BookingBundle\Price") */
    private $price;
    }

    View Slide

  81. 5.
    Doctrine Support

    View Slide

  82. Serializing a
    Doctrine entity

    View Slide

  83. class BookingController extends Controller
    {
    /**
    * @Route(
    * "/booking/{reference}.{_format}",
    * defaults = { "_format" = "xml" }
    * )
    */
    public function showAction(Booking $booking)
    {
    $serializer = $this->container->get('serializer');
    $xml = $serializer->serialize($booking, 'xml');
    return new Response($xml);
    }
    }

    View Slide



  84. 1


    440
    86.24




    1


    130


    5


    90



    View Slide

  85. Serializing a
    Doctrine
    Collection

    View Slide

  86. class BookingController extends Controller
    {
    public function indexAction()
    {
    $em = $this->get('doctrine')->getManager();
    $bookings = $em
    ->getRepository('AcmeBookingBundle:Booking')
    ->findAll()
    ;
    $serializer = $this->container->get('serializer');
    $xml = $serializer->serialize($bookings, 'xml');
    return new Response($xml);
    }
    }

    View Slide

  87. View Slide

  88. Unserializing a
    Doctrine entity

    View Slide

  89. $json = '{"id":1,"reference":"GH45IZ8","arrival":"12\/
    23\/2012","departure":"12\/25\/2012","amount":
    440,"vat":86.24,"currency":"EUR","customer":{"id": 1,
    "username":"hhamon"},"rooms":[{"id":
    1,"number":"420","type":"double","price":130},{"id":
    5,"number":"518","type":"single","price":90}]}';
    $serializer = $this->container->get('serializer');
    $booking = $serializer->deserialize(
    $json,
    'Acme\BookingBundle\Entity\Booking',
    'json'
    );

    View Slide

  90. https://joind.in/talk/view/7568

    View Slide