diff --git a/components/index.rst b/components/index.rst index 43aa14e5e40..8f8cdc08f35 100644 --- a/components/index.rst +++ b/components/index.rst @@ -28,7 +28,7 @@ The Components property_access/index routing/index security/index - serializer + serializer/index stopwatch templating/index translation/index diff --git a/components/map.rst.inc b/components/map.rst.inc index ee9c34ba884..472b68f1c11 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -140,9 +140,11 @@ * :doc:`/components/security/authorization` * :doc:`/components/security/secure_tools` -* **Serializer** +* :doc:`/components/serializer/index` - * :doc:`/components/serializer` + * :doc:`/components/serializer/introduction` + * :doc:`/components/serializer/encoders` + * :doc:`/components/serializer/normalizers` * **Stopwatch** diff --git a/components/serializer.rst b/components/serializer.rst deleted file mode 100644 index c8354d9713e..00000000000 --- a/components/serializer.rst +++ /dev/null @@ -1,641 +0,0 @@ -.. index:: - single: Serializer - single: Components; Serializer - -The Serializer Component -======================== - - The Serializer component is meant to be used to turn objects into a - specific format (XML, JSON, YAML, ...) and the other way around. - -In order to do so, the Serializer component follows the following -simple schema. - -.. _component-serializer-encoders: -.. _component-serializer-normalizers: - -.. image:: /images/components/serializer/serializer_workflow.png - -As you can see in the picture above, an array is used as a man in -the middle. This way, Encoders will only deal with turning specific -**formats** into **arrays** and vice versa. The same way, Normalizers -will deal with turning specific **objects** into **arrays** and vice versa. - -Serialization is a complicated topic, and while this component may not work -in all cases, it can be a useful tool while developing tools to serialize -and deserialize your objects. - -Installation ------------- - -You can install the component in 2 different ways: - -* :doc:`Install it via Composer ` (``symfony/serializer`` on `Packagist`_); -* Use the official Git repository (https://github.com/symfony/serializer). - - -.. include:: /components/require_autoload.rst.inc - -To use the ``ObjectNormalizer``, the :doc:`PropertyAccess component ` -must also be installed. - -Usage ------ - -Using the Serializer component is really simple. You just need to set up -the :class:`Symfony\\Component\\Serializer\\Serializer` specifying -which Encoders and Normalizer are going to be available:: - - use Symfony\Component\Serializer\Serializer; - use Symfony\Component\Serializer\Encoder\XmlEncoder; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - - $encoders = array(new XmlEncoder(), new JsonEncoder()); - $normalizers = array(new ObjectNormalizer()); - - $serializer = new Serializer($normalizers, $encoders); - -The preferred normalizer is the -:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`, but other -normalizers are available. -To read more about them, refer to the `Normalizers`_ section of this page. All -the examples shown below use the ``ObjectNormalizer``. - -Serializing an Object ---------------------- - -For the sake of this example, assume the following class already -exists in your project:: - - namespace Acme; - - class Person - { - private $age; - private $name; - private $sportsman; - - // Getters - public function getName() - { - return $this->name; - } - - public function getAge() - { - return $this->age; - } - - // Issers - public function isSportsman() - { - return $this->sportsman; - } - - // Setters - public function setName($name) - { - $this->name = $name; - } - - public function setAge($age) - { - $this->age = $age; - } - - public function setSportsman($sportsman) - { - $this->sportsman = $sportsman; - } - } - -Now, if you want to serialize this object into JSON, you only need to -use the Serializer service created before:: - - $person = new Acme\Person(); - $person->setName('foo'); - $person->setAge(99); - $person->setSportsman(false); - - $jsonContent = $serializer->serialize($person, 'json'); - - // $jsonContent contains {"name":"foo","age":99,"sportsman":false} - - echo $jsonContent; // or return it in a Response - -The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer::serialize` -is the object to be serialized and the second is used to choose the proper encoder, -in this case :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`. - -Deserializing an Object ------------------------ - -You'll now learn how to do the exact opposite. This time, the information -of the ``Person`` class would be encoded in XML format:: - - $data = << - foo - 99 - false - - EOF; - - $person = $serializer->deserialize($data, 'Acme\Person', 'xml'); - -In this case, :method:`Symfony\\Component\\Serializer\\Serializer::deserialize` -needs three parameters: - -#. The information to be decoded -#. The name of the class this information will be decoded to -#. The encoder used to convert that information into an array - -Deserializing in an Existing Object -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The serializer can also be used to update an existing object:: - - $person = new Acme\Person(); - $person->setName('bar'); - $person->setAge(99); - $person->setSportsman(true); - - $data = << - foo - 69 - - EOF; - - $serializer->deserialize($data, 'Acme\Person', 'xml', array('object_to_populate' => $person)); - // $person = Acme\Person(name: 'foo', age: '69', sportsman: true) - -This is a common need when working with an ORM. - -.. _component-serializer-attributes-groups: - -Attributes Groups ------------------ - -.. versionadded:: 2.7 - The support of serialization and deserialization groups was introduced - in Symfony 2.7. - -Sometimes, you want to serialize different sets of attributes from your -entities. Groups are a handy way to achieve this need. - -Assume you have the following plain-old-PHP object:: - - namespace Acme; - - class MyObj - { - public $foo; - - private $bar; - - public function getBar() - { - return $this->bar; - } - - public function setBar($bar) - { - return $this->bar = $bar; - } - } - -The definition of serialization can be specified using annotations, XML -or YAML. The :class:`Symfony\\Component\\Serializer\\Mapping\\Factory\\ClassMetadataFactory` -that will be used by the normalizer must be aware of the format to use. - -Initialize the :class:`Symfony\\Component\\Serializer\\Mapping\\Factory\\ClassMetadataFactory` -like the following:: - - use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; - // For annotations - use Doctrine\Common\Annotations\AnnotationReader; - use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; - // For XML - // use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; - // For YAML - // use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; - - $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - // For XML - // $classMetadataFactory = new ClassMetadataFactory(new XmlFileLoader('/path/to/your/definition.xml')); - // For YAML - // $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yml')); - -.. _component-serializer-attributes-groups-annotations: - -Then, create your groups definition: - -.. configuration-block:: - - .. code-block:: php-annotations - - namespace Acme; - - use Symfony\Component\Serializer\Annotation\Groups; - - class MyObj - { - /** - * @Groups({"group1", "group2"}) - */ - public $foo; - - /** - * @Groups({"group3"}) - */ - public function getBar() // is* methods are also supported - { - return $this->bar; - } - - // ... - } - - .. code-block:: yaml - - Acme\MyObj: - attributes: - foo: - groups: ['group1', 'group2'] - bar: - groups: ['group3'] - - .. code-block:: xml - - - - - - group1 - group2 - - - - group3 - - - - -You are now able to serialize only attributes in the groups you want:: - - use Symfony\Component\Serializer\Serializer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - - $obj = new MyObj(); - $obj->foo = 'foo'; - $obj->setBar('bar'); - - $normalizer = new ObjectNormalizer($classMetadataFactory); - $serializer = new Serializer(array($normalizer)); - - $data = $serializer->normalize($obj, null, array('groups' => array('group1'))); - // $data = array('foo' => 'foo'); - - $obj2 = $serializer->denormalize( - array('foo' => 'foo', 'bar' => 'bar'), - 'MyObj', - null, - array('groups' => array('group1', 'group3')) - ); - // $obj2 = MyObj(foo: 'foo', bar: 'bar') - -.. _ignoring-attributes-when-serializing: - -Ignoring Attributes -------------------- - -.. note:: - - Using attribute groups instead of the :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` - method is considered best practice. - -.. versionadded:: 2.3 - The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` - method was introduced in Symfony 2.3. - -.. versionadded:: 2.7 - Prior to Symfony 2.7, attributes were only ignored while serializing. Since Symfony - 2.7, they are ignored when deserializing too. - -As an option, there's a way to ignore attributes from the origin object. To remove -those attributes use the -:method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` -method on the normalizer definition:: - - use Symfony\Component\Serializer\Serializer; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - - $normalizer = new ObjectNormalizer(); - $normalizer->setIgnoredAttributes(array('age')); - $encoder = new JsonEncoder(); - - $serializer = new Serializer(array($normalizer), array($encoder)); - $serializer->serialize($person, 'json'); // Output: {"name":"foo","sportsman":false} - -Converting Property Names when Serializing and Deserializing ------------------------------------------------------------- - -.. versionadded:: 2.7 - The :class:`Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface` - interface was introduced in Symfony 2.7. - -Sometimes serialized attributes must be named differently than properties -or getter/setter methods of PHP classes. - -The Serializer Component provides a handy way to translate or map PHP field -names to serialized names: The Name Converter System. - -Given you have the following object:: - - class Company - { - public $name; - public $address; - } - -And in the serialized form, all attributes must be prefixed by ``org_`` like -the following:: - - {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"} - -A custom name converter can handle such cases:: - - use Symfony\Component\Serializer\NameConverter\NameConverterInterface; - - class OrgPrefixNameConverter implements NameConverterInterface - { - public function normalize($propertyName) - { - return 'org_'.$propertyName; - } - - public function denormalize($propertyName) - { - // remove org_ prefix - return 'org_' === substr($propertyName, 0, 4) ? substr($propertyName, 4) : $propertyName; - } - } - -The custom normalizer can be used by passing it as second parameter of any -class extending :class:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer`, -including :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` -and :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`:: - - use Symfony\Component\Serializer\Encoder\JsonEncoder - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $nameConverter = new OrgPrefixNameConverter(); - $normalizer = new ObjectNormalizer(null, $nameConverter); - - $serializer = new Serializer(array($normalizer), array(new JsonEncoder())); - - $obj = new Company(); - $obj->name = 'Acme Inc.'; - $obj->address = '123 Main Street, Big City'; - - $json = $serializer->serialize($obj); - // {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"} - $objCopy = $serializer->deserialize($json); - // Same data as $obj - -.. _using-camelized-method-names-for-underscored-attributes: - -CamelCase to snake_case -~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.7 - The :class:`Symfony\\Component\\Serializer\\NameConverter\\CamelCaseToSnakeCaseNameConverter` - interface was introduced in Symfony 2.7. - -In many formats, it's common to use underscores to separate words (also known -as snake_case). However, PSR-1 specifies that the preferred style for PHP -properties and methods is CamelCase. - -Symfony provides a built-in name converter designed to transform between -snake_case and CamelCased styles during serialization and deserialization -processes:: - - use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - - $normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); - - class Person - { - private $firstName; - - public function __construct($firstName) - { - $this->firstName = $firstName; - } - - public function getFirstName() - { - return $this->firstName; - } - } - - $kevin = new Person('Kévin'); - $normalizer->normalize($kevin); - // ['first_name' => 'Kévin']; - - $anne = $normalizer->denormalize(array('first_name' => 'Anne'), 'Person'); - // Person object with firstName: 'Anne' - -Serializing Boolean Attributes ------------------------------- - -If you are using isser methods (methods prefixed by ``is``, like -``Acme\Person::isSportsman()``), the Serializer component will automatically -detect and use it to serialize related attributes. - -The ``ObjectNormalizer`` also takes care of methods starting with ``has``, ``add`` -and ``remove``. - -Using Callbacks to Serialize Properties with Object Instances -------------------------------------------------------------- - -When serializing, you can set a callback to format a specific object property:: - - use Acme\Person; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; - use Symfony\Component\Serializer\Serializer; - - $encoder = new JsonEncoder(); - $normalizer = new GetSetMethodNormalizer(); - - $callback = function ($dateTime) { - return $dateTime instanceof \DateTime - ? $dateTime->format(\DateTime::ISO8601) - : ''; - }; - - $normalizer->setCallbacks(array('createdAt' => $callback)); - - $serializer = new Serializer(array($normalizer), array($encoder)); - - $person = new Person(); - $person->setName('cordoval'); - $person->setAge(34); - $person->setCreatedAt(new \DateTime('now')); - - $serializer->serialize($person, 'json'); - // Output: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"} - -Normalizers ------------ - -There are several types of normalizers available: - -:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` - This normalizer leverages the :doc:`PropertyAccess Component ` - to read and write in the object. It means that it can access to properties - directly and through getters, setters, hassers, adders and removers. It supports - calling the constructor during the denormalization process. - - Objects are normalized to a map of property names (method name stripped of - the "get"/"set"/"has"/"remove" prefix and converted to lower case) to property - values. - - The ``ObjectNormalizer`` is the most powerful normalizer. It is a configured - by default when using the Symfony Standard Edition with the serializer enabled. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` - This normalizer reads the content of the class by calling the "getters" - (public methods starting with "get"). It will denormalize data by calling - the constructor and the "setters" (public methods starting with "set"). - - Objects are normalized to a map of property names (method name stripped of - the "get" prefix and converted to lower case) to property values. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` - This normalizer directly reads and writes public properties as well as - **private and protected** properties. It supports calling the constructor - during the denormalization process. - - Objects are normalized to a map of property names to property values. - -.. versionadded:: 2.6 - The :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` - class was introduced in Symfony 2.6. - -.. versionadded:: 2.7 - The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` - class was introduced in Symfony 2.7. - -Handling Circular References ----------------------------- - -.. versionadded:: 2.6 - Handling of circular references was introduced in Symfony 2.6. In previous - versions of Symfony, circular references led to infinite loops. - -Circular references are common when dealing with entity relations:: - - class Organization - { - private $name; - private $members; - - public function setName($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - - public function setMembers(array $members) - { - $this->members = $members; - } - - public function getMembers() - { - return $this->members; - } - } - - class Member - { - private $name; - private $organization; - - public function setName($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - - public function setOrganization(Organization $organization) - { - $this->organization = $organization; - } - - public function getOrganization() - { - return $this->organization; - } - } - -To avoid infinite loops, :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` -throws a :class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException` -when such a case is encountered:: - - $member = new Member(); - $member->setName('Kévin'); - - $org = new Organization(); - $org->setName('Les-Tilleuls.coop'); - $org->setMembers(array($member)); - - $member->setOrganization($org); - - echo $serializer->serialize($org, 'json'); // Throws a CircularReferenceException - -The ``setCircularReferenceLimit()`` method of this normalizer sets the number -of times it will serialize the same object before considering it a circular -reference. Its default value is ``1``. - -Instead of throwing an exception, circular references can also be handled -by custom callables. This is especially useful when serializing entities -having unique identifiers:: - - $encoder = new JsonEncoder(); - $normalizer = new ObjectNormalizer(); - - $normalizer->setCircularReferenceHandler(function ($object) { - return $object->getName(); - }); - - $serializer = new Serializer(array($normalizer), array($encoder)); - var_dump($serializer->serialize($org, 'json')); - // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} - -.. seealso:: - - A popular alternative to the Symfony Serializer Component is the third-party - library, `JMS serializer`_ (released under the Apache license, so incompatible with GPLv2 projects). - -.. _`JMS serializer`: https://github.com/schmittjoh/serializer -.. _Packagist: https://packagist.org/packages/symfony/serializer diff --git a/components/serializer/encoders.rst b/components/serializer/encoders.rst new file mode 100644 index 00000000000..8df13b97bd2 --- /dev/null +++ b/components/serializer/encoders.rst @@ -0,0 +1,105 @@ +.. index:: + single: Serializer, Encoders + +Encoders +======== + +Encoders basically turn **arrays** into **formats** and vice versa. +They implement :class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` for encoding (array to format) and :class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface` for decoding (format to array). + +You can add new encoders to a Serializer instance by using its second constructor argument:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Encoder\XmlEncoder; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + + $encoders = array(new XmlEncoder(), new JsonEncoder()); + $serializer = new Serializer(array(), $encoders); + +Built-in encoders +----------------- + +You can see in the example above that we use two encoders: + +* :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder` to encode/decode XML +* :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` to encode/decode JSON + +The ``XmlEncoder`` +~~~~~~~~~~~~~~~~~~ + +This encoder transform arrays into XML and vice versa. + +For example, we will guess that you have an object normalized as following:: + + array('foo' => array(1, 2), 'bar' => true); + +The ``XmlEncoder`` will encode this object like that:: + + + + 1 + 2 + 1 + + +Be aware that this encoder will consider keys beginning with ``@`` as attributes:: + + $encoder = new XmlEncoder(); + $encoder->encode(array('foo' => array('@bar' => 'value'))); + // will return: + // + // + // + // + +The ``JsonEncoder`` +~~~~~~~~~~~~~~~~~~~ + +The ``JsonEncoder`` is much simpler and is based on the PHP `json_encode`_ and `json_decode`_ functions. + +.. _json_encode: https://secure.php.net/manual/fr/function.json-encode.php +.. _json_decode: https://secure.php.net/manual/fr/function.json-decode.php + +Custom encoders +--------------- + +If you need to support another format than XML and JSON, you can create your own encoder. +We will guess that you want to serialize and deserialize Yaml. For that, we will use +:doc:`/components/yaml/index`:: + + namespace App\Encoder; + + use Symfony\Component\Serializer\Encoder\DecoderInterface; + use Symfony\Component\Serializer\Encoder\EncoderInterface; + use Symfony\Component\Yaml\Yaml; + + class YamlEncoder implements EncoderInterface, DecoderInterface + { + public function encode($data, $format, array $context = array()) + { + return Yaml::dump($data); + } + + public function supportsEncoding($format) + { + return 'json' === $format; + } + + public function decode($data, $format, array $context = array()) + { + return Yaml::parse($data); + } + + public function supportsDecoding($format) + { + return 'json' === $format; + } + } + +Then just pass it to your serializer:: + + use Symfony\Component\Serializer\Serializer; + + $serializer = new Serializer(array(), array(new App\Encoder\YamlEncoder())); + +Now you'll be able to serialize and deserialize Yaml. diff --git a/components/serializer/index.rst b/components/serializer/index.rst new file mode 100644 index 00000000000..f79113aed18 --- /dev/null +++ b/components/serializer/index.rst @@ -0,0 +1,9 @@ +Serializer +========== + +.. toctree:: + :maxdepth: 2 + + introduction + encoders + normalizers diff --git a/components/serializer/introduction.rst b/components/serializer/introduction.rst new file mode 100644 index 00000000000..afa920b6fbf --- /dev/null +++ b/components/serializer/introduction.rst @@ -0,0 +1,307 @@ +.. index:: + single: Serializer + single: Components; Serializer + +The Serializer Component +======================== + + The Serializer component is meant to be used to turn objects into a + specific format (XML, JSON, YAML, ...) and the other way around. + +In order to do so, the Serializer component follows the following +simple schema. + +.. _component-serializer-encoders: +.. _component-serializer-normalizers: + +.. image:: /images/components/serializer/serializer_workflow.png + +As you can see in the picture above, an array is used as a man in +the middle. This way, Encoders will only deal with turning specific +**formats** into **arrays** and vice versa. The same way, Normalizers +will deal with turning specific **objects** into **arrays** and vice versa. + +Serialization is a complicated topic, and while this component may not work +in all cases, it can be a useful tool while developing tools to serialize +and deserialize your objects. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/serializer`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/serializer). + + +.. include:: /components/require_autoload.rst.inc + +To use the ``ObjectNormalizer``, the :doc:`PropertyAccess component ` +must also be installed. + +Usage +----- + +Using the Serializer component is really simple. You just need to set up +the :class:`Symfony\\Component\\Serializer\\Serializer` specifying +which Encoders and Normalizer are going to be available:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Encoder\XmlEncoder; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + + // Allows to serialize/deserialize in JSON and XML + $encoders = array(new XmlEncoder(), new JsonEncoder()); + $normalizers = array(new ObjectNormalizer()); + + $serializer = new Serializer($normalizers, $encoders); + +The preferred normalizer is the +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`, but other +normalizers are available. + +.. note:: + + Read the dedicated sections to learn more about :doc:`/components/serializer/encoders` + and :doc:`/components/serializer/normalizers`. + +Serializing an Object +--------------------- + +For the sake of this example, assume the following class already +exists in your project:: + + namespace Acme; + + class Person + { + private $age; + private $name; + private $sportsman; + + // Getters + public function getName() + { + return $this->name; + } + + public function getAge() + { + return $this->age; + } + + // Issers + public function isSportsman() + { + return $this->sportsman; + } + + // Setters + public function setName($name) + { + $this->name = $name; + } + + public function setAge($age) + { + $this->age = $age; + } + + public function setSportsman($sportsman) + { + $this->sportsman = $sportsman; + } + } + +Now, if you want to serialize this object into JSON, you only need to +use the Serializer service created before:: + + $person = new Acme\Person(); + $person->setName('foo'); + $person->setAge(99); + $person->setSportsman(false); + + $jsonContent = $serializer->serialize($person, 'json'); + + // $jsonContent contains {"name":"foo","age":99,"sportsman":false} + + echo $jsonContent; // or return it in a Response + +The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer::serialize` +is the object to be serialized and the second is used to choose the proper encoder, +in this case :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`. + +Deserializing an Object +----------------------- + +You'll now learn how to do the exact opposite. This time, the information +of the ``Person`` class would be encoded in XML format:: + + $data = << + foo + 99 + false + + EOF; + + // Will return an instance of Acme\Person + $person = $serializer->deserialize($data, 'Acme\Person', 'xml'); + +In this case, :method:`Symfony\\Component\\Serializer\\Serializer::deserialize` +needs three parameters: + +#. The information to be decoded +#. The name of the class this information will be decoded to +#. The encoder used to convert that information into an array + +Go further +---------- + +If this is not already done, you should take a look at :doc:`/components/serializer/encoders` +and :doc:`/components/serializer/normalizers` to be able to use the entire abilities of this component. + +Normalizers +----------- + +There are several types of normalizers available: + +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + This normalizer leverages the :doc:`PropertyAccess Component ` + to read and write in the object. It means that it can access to properties + directly and through getters, setters, hassers, adders and removers. It supports + calling the constructor during the denormalization process. + + Objects are normalized to a map of property names (method name stripped of + the "get"/"set"/"has"/"remove" prefix and converted to lower case) to property + values. + + The ``ObjectNormalizer`` is the most powerful normalizer. It is a configured + by default when using the Symfony Standard Edition with the serializer enabled. + +:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` + This normalizer reads the content of the class by calling the "getters" + (public methods starting with "get"). It will denormalize data by calling + the constructor and the "setters" (public methods starting with "set"). + + Objects are normalized to a map of property names (method name stripped of + the "get" prefix and converted to lower case) to property values. + +:class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` + This normalizer directly reads and writes public properties as well as + **private and protected** properties. It supports calling the constructor + during the denormalization process. + + Objects are normalized to a map of property names to property values. + +.. versionadded:: 2.6 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` + class was introduced in Symfony 2.6. + +.. versionadded:: 2.7 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + class was introduced in Symfony 2.7. + +Handling Circular References +---------------------------- + +.. versionadded:: 2.6 + Handling of circular references was introduced in Symfony 2.6. In previous + versions of Symfony, circular references led to infinite loops. + +Circular references are common when dealing with entity relations:: + + class Organization + { + private $name; + private $members; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setMembers(array $members) + { + $this->members = $members; + } + + public function getMembers() + { + return $this->members; + } + } + + class Member + { + private $name; + private $organization; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setOrganization(Organization $organization) + { + $this->organization = $organization; + } + + public function getOrganization() + { + return $this->organization; + } + } + +To avoid infinite loops, :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` +throws a :class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException` +when such a case is encountered:: + + $member = new Member(); + $member->setName('Kévin'); + + $org = new Organization(); + $org->setName('Les-Tilleuls.coop'); + $org->setMembers(array($member)); + + $member->setOrganization($org); + + echo $serializer->serialize($org, 'json'); // Throws a CircularReferenceException + +The ``setCircularReferenceLimit()`` method of this normalizer sets the number +of times it will serialize the same object before considering it a circular +reference. Its default value is ``1``. + +Instead of throwing an exception, circular references can also be handled +by custom callables. This is especially useful when serializing entities +having unique identifiers:: + + $encoder = new JsonEncoder(); + $normalizer = new ObjectNormalizer(); + + $normalizer->setCircularReferenceHandler(function ($object) { + return $object->getName(); + }); + + $serializer = new Serializer(array($normalizer), array($encoder)); + var_dump($serializer->serialize($org, 'json')); + // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} + +.. seealso:: + + A popular alternative to the Symfony Serializer Component is the third-party + library, `JMS serializer`_ (released under the Apache license, so incompatible with GPLv2 projects). + +.. _`JMS serializer`: https://github.com/schmittjoh/serializer +.. _Packagist: https://packagist.org/packages/symfony/serializer diff --git a/components/serializer/normalizers.rst b/components/serializer/normalizers.rst new file mode 100644 index 00000000000..1b349c8e778 --- /dev/null +++ b/components/serializer/normalizers.rst @@ -0,0 +1,23 @@ +.. index:: + single: Security, Normalizers + +Normalizers +=========== + +Normalizers turn **objects** into **arrays** and vice-versa. +They implement :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface` for normalizing (object to array) and :class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizerInterface` for denormalizing (array to object). + +You can add new normalizers to a Serializer instance by using its first constructor argument:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; + use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + + $normalizers = array(new ArrayDenormalizer(), new GetSetMethodNormalizer()); + $serializer = new Serializer($normalizers); + +Built-in encoders +----------------- + +The Serializer Component provides several normalizers for most use cases: + * diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 9bf60060c1a..0c17eb0dba8 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -6,11 +6,11 @@ How to Use the Serializer Serializing and deserializing to and from objects and different formats (e.g. JSON or XML) is a very complex topic. Symfony comes with a -:doc:`Serializer Component `, which gives you some +:doc:`Serializer Component`, which gives you some tools that you can leverage for your solution. In fact, before you start, get familiar with the serializer, normalizers -and encoders by reading the :doc:`Serializer Component `. +and encoders by reading the :doc:`Serializer Component`. Activating the Serializer ------------------------- diff --git a/redirection_map b/redirection_map index faf3e0fd638..caa4417548f 100644 --- a/redirection_map +++ b/redirection_map @@ -24,6 +24,7 @@ /components/routing /components/routing/introduction /cookbook/console/generating_urls /cookbook/console/sending_emails /components/yaml /components/yaml/introduction +/components/serializer /components/serializer/introduction /components/templating /components/templating/introduction /components/filesystem /components/filesystem/introduction /cmf/reference/configuration/block /cmf/bundles/block/configuration