Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] Serializer improvements #14924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GuilhemN opened this issue Jun 9, 2015 · 7 comments
Closed

[Serializer] Serializer improvements #14924

GuilhemN opened this issue Jun 9, 2015 · 7 comments

Comments

@GuilhemN
Copy link
Contributor

GuilhemN commented Jun 9, 2015

The symfony serializer is a great tool but it's probably underused now.

2.7 introduces the groups and I think that we could think to else fields we could create !

Firstly, the fields exposition isn't really present now. I suggest to add an Expose config (definable to true/false) and the possibility to change a field serialized name ExportsAs.
It could also be useful to implement virtual properties.

Besides that I think it's important to have the choice to split readable field and writable fields:
So I suggest to have a normal policy NormalizePolicy, a readable policy ReadablePolicy and a writable policy WritablePolicy on classes that define a default exposition, a default security (expression, overwritable) and a global security (expression, not overwritable).

We should develop maybe a verification mechanism too with a type verification and normalization.

If you don't see what I mean, I created some annotation classes : https://github.com/Ener-Getick/symfony/tree/ticket_14924/src/Symfony/Component/Serializer/Annotation.

Please give your feedbacks.

@dunglas
Copy link
Member

dunglas commented Jun 15, 2015

It's not the current philosophy of the Serializer component. Adding such features is IMO a bad idea:

  • it creates a bunch of ways to do things that can already be done currently
  • it make the component harder to maintain
  • it bloats (and probably slow down) the component

Expose config (definable to true/false)

You can already use groups to do that. What's the use case not covered with the existing feature?

change a field serialized name ExportsAs

It's IMO a bad idea to use annotations for that (encourage inconsistencies). The name converter system can already be used to do such things: http://symfony.com/doc/current/components/serializer.html#converting-property-names-when-serializing-and-deserializing

It could also be useful to implement virtual properties

I don't like that kind of things:

  • it's doable only for normalization, not for denormalization
  • it introduces a lot of magic
  • it's more clear and easy to do this using raw PHP (and groups if necessary)

Besides that I think it's important to have the choice to split readable field and writable fields

Here again, this can definitely be done with groups:

use Symfony\Component\Serializer\Annotation\Groups;

class Test
{
    /** @Groups{"read"} */
    public $foo;
    /** @Groups{"read", "write"} */
    public $bar;
}

You can even adopt fine grained policy:

use Symfony\Component\Serializer\Annotation\Groups;

class Test
{
    /** @Groups{"user-read", "admin-read", "admin-write"} */
    public $foo;
    /** @Groups{"user-read", "user-write", "admin-read", "admin-write"} */
    public $bar;
}

We should develop maybe a verification mechanism too with a type verification and normalization.

Agree. There is work in progress on that topic: #14844 (comment)

@dunglas
Copy link
Member

dunglas commented Jun 15, 2015

For the ExportsAs feature. It can be done using a special name converter aware of the ClassMetadata.

@dunglas
Copy link
Member

dunglas commented Jun 15, 2015

However, an improvement I want to add is a @Depth annotation. I've described the use case here: api-platform/core#104 (comment)

What do you think?

@GuilhemN
Copy link
Contributor Author

I didn't really explained why I think that the virtual properties are a good idea ...
Now, you have to add a @Groups annotation on a method to serialized it.
But I think it's not really clear and it would be better to centralize that in a @VirtualProperties annotation.

An example :

/**
 * @VirtualProperties({
 *    "foo": {
 *        @Readable(getter="notUsualSetter"),
 *        @Writable(setter="getUserFoo", security="is_granted('ROLE_SUPER_ADMIN')")
 *    },
 *    "bar": {
 *        @Expose(security="is_granted('ROLE_USER')")
 *    }
 * })
 * @NormalizePolicy(defaultExposition=false)
 */
class FooClass
{
    ...
}

With the virtual properties, you can define for each method how it will be serialize and not just for the methods get|set|has|is. Moreover you don't have to scroll on your entire class to find what you want.

For the exposition features, they are principally made to simplify the development and to don't have to worry about the security questions. (For example the plainPassword of the FOSUserBundle is writable in some cases but not readable.)

For the ExportsAs, I don't know finally if it was a great idea. It could create inconsistencies but in some cases, you want to change a field name and that's maybe too hard for a lot of people to create a new name converter ?

@GuilhemN
Copy link
Contributor Author

The @Depth annotation could be really useful in some cases.
I just don't know if it's better to throw an exception or to do nothing because it's the user who defines the depth.
It could be fine to add a custom parameter to the annotation to define the comportment for each field and also add a general parameter that defines the default comportment.

Something like @Depth(2, comportment="exception") with comportment that could be default, exception or invisible.

@dunglas
Copy link
Member

dunglas commented Jun 15, 2015

About @Depth, it will have the same behavior as for circular reference: allow to register a custom handler (a \Callable) and have a default handler that throw an exception.

@afurculita
Copy link
Contributor

@Ener-Getick @Alias annotation is in work in #15200 to implement the behaviour you described for @ExportsAs

@GuilhemN GuilhemN closed this as completed Oct 5, 2015
fabpot added a commit that referenced this issue Jan 26, 2016
This PR was squashed before being merged into the 3.1-dev branch (closes #17113).

Discussion
----------

[Serializer] Add a MaxDepth option

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #14924 (comment), api-platform/core#104 (comment)
| License       | MIT
| Doc PR        | todo

Add a max depth option during the normalization process. Especially useful when normalizing trees.

Usage:

```php
use Symfony\Component\Serializer\Annotation\MaxDepth;

class MyObj
{
    /**
     * @MaxDepth(2)
     */
    public $foo;

    /**
     * @var self
     */
    public $child;
}

use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;

$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($this->normalizer));

$level1 = new MyObj();
$level1->foo = 'level1';

$level2 = new MyObj();
$level2->foo = 'level2';
$level1->child = $level2;

$level3 = new MyObj();
$level3->foo = 'level3';
$level2->child = $level3;
$result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true));
/*      $result = array(
            'foo' => 'level1',
            'child' => array(
                    'foo' => 'level2',
                    'child' => array(
                            'child' => null,
                        ),
                ),
        );
*/
```

* [x] Metadata support
* [x] `@MaxDepth(2)` annotation and loader
* [x] XML loader
* [x] YAML loader
* [x] Delegate recursive normalization at the end of the process
* [x] Use constants with Late Static Binding in the abstract class instead of raw strings
* [x] Move common logic to the abstract class

/cc @mRoca @csarrazi

Commits
-------

a44bead [Serializer] Add a MaxDepth option
symfony-splitter pushed a commit to symfony/serializer that referenced this issue Jan 26, 2016
This PR was squashed before being merged into the 3.1-dev branch (closes #17113).

Discussion
----------

[Serializer] Add a MaxDepth option

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | symfony/symfony#14924 (comment), api-platform/core#104 (comment)
| License       | MIT
| Doc PR        | todo

Add a max depth option during the normalization process. Especially useful when normalizing trees.

Usage:

```php
use Symfony\Component\Serializer\Annotation\MaxDepth;

class MyObj
{
    /**
     * @MaxDepth(2)
     */
    public $foo;

    /**
     * @var self
     */
    public $child;
}

use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;

$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($this->normalizer));

$level1 = new MyObj();
$level1->foo = 'level1';

$level2 = new MyObj();
$level2->foo = 'level2';
$level1->child = $level2;

$level3 = new MyObj();
$level3->foo = 'level3';
$level2->child = $level3;
$result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true));
/*      $result = array(
            'foo' => 'level1',
            'child' => array(
                    'foo' => 'level2',
                    'child' => array(
                            'child' => null,
                        ),
                ),
        );
*/
```

* [x] Metadata support
* [x] `@MaxDepth(2)` annotation and loader
* [x] XML loader
* [x] YAML loader
* [x] Delegate recursive normalization at the end of the process
* [x] Use constants with Late Static Binding in the abstract class instead of raw strings
* [x] Move common logic to the abstract class

/cc @mRoca @csarrazi

Commits
-------

a44bead [Serializer] Add a MaxDepth option
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants