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

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. Serialization is the process of converting an object state into

    a format that can be stored and resurrected later Wikipedia  
  2. 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); }
  3. 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); }
  4. 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); }
  5. 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); }
  6. 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); }
  7. 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);
  8. $xml = '<response>...</response>'; // 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') ;
  9. 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; } }
  10. 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; } }
  11. $yaml = <<<EOY 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();
  12. $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');
  13. {# uses JSON #} {{ booking | serialize }} {#

    Custom output format #} {{ booking | serialize('json') }} {{ booking | serialize('xml') }} {{ booking | serialize('yml') }}
  14. # 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 # ...
  15. # AcmeBookingBundle\Resources\config\serializer\Booking.xml <?xml version="1.0" encoding="UTF-8" ?> <serializer> <class name="Acme\BookingBundle\Booking" exclusion-policy="ALL"

    xml-root-name="booking" access-type="public_method" > <property name="reference" expose="true" type="string"/> <property name="amount" expose="true" type="double"/> </class> </serializer>
  16. namespace Acme\BookingBundle; use JMS\SerializerBundle\Annotation as Serialize; /** * @Serialize\XmlRoot("booking") */

    class Booking { /** * @Serialize\XmlAttribute * @Serialize\Type("string") */ private $reference; // ... }
  17. class Price { /** @Serialize\XmlValue */ private $amount; /** @Serialize\XmlAttribute

    */ private $vat; /** @Serialize\XmlAttribute */ private $currency; }
  18. $booking = new Booking(); $booking->setReference('SLT-123456'); // ... $booking->setPrice(new Price(120, 12,

    'EUR')); <?xml version="1.0" encoding="UTF-8"?> <booking> <!-- ... --> <departure>2012-09-29</departure> <price vat="12" currency="EUR">120</price> </booking>
  19. 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]); } } }
  20. // ... class Booking { /** @Serialize\Groups({ "list" }) */

    private $id; /** @Serialize\Groups({ "list", "details" }) */ private $reference; /** @Serialize\Groups({ "details" }) */ private $designation; }
  21. 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'); } }
  22. class Booking { /** * @Serialize\Accessor( * setter = "setShortDesignation",

    * getter = "getShortDesignation" * ) * */ private $designation; }
  23. /** * @Serialize\AccessOrder(custom = { * "reference", * "arrival", *

    "departure" * }) * */ class Booking { // ... }
  24. class Booking { /** @Serialize\Type("string") */ private $reference; /** @Serialize\Type("DateTime")

    */ private $arrival; /** @Serialize\Type("Acme\BookingBundle\Price") */ private $price; }
  25. 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); } }
  26. <?xml version="1.0" encoding="UTF-8"?> <booking reference="GH45IZ8"> <id>1</id> <arrival><![CDATA[12/23/2012]]></arrival> <departure><![CDATA[12/25/2012]]></departure> <amount>440</amount> <vat>86.24</vat>

    <currency><![CDATA[EUR]]></currency> <customer username="hhamon"/> <rooms> <entry> <id>1</id> <number><![CDATA[420]]></number> <type><![CDATA[double]]></type> <price>130</price> </entry> <entry> <id>5</id> <number><![CDATA[518]]></number> <type><![CDATA[single]]></type> <price>90</price> </entry> </rooms> </booking>
  27. 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); } }