Skip to content

[Serializer] Name converter support #13120

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

Merged
merged 2 commits into from
Feb 3, 2015
Merged

Conversation

dunglas
Copy link
Member

@dunglas dunglas commented Dec 25, 2014

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #12212
License MIT
Doc PR symfony/symfony-docs#4692

This PR adds support for custom property naming strategies to the serializer and provides a built-in NameConverter using this new system: (CamelCase to underscore).
It handles normalization and denormalization (convert fooBar to foo_bar when serializing, then from foo_bar to fooBar when deserializing). It also has a flag to convert only some attributes.

The setCamelizedAttributes() is deprecated in favor of this new method (more flexible, allows to rename all attributes of a class and support deserialization) and now uses it internally.

*/
public function __construct(ClassMetadataFactory $classMetadataFactory = null)
{
public function __construct(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor signature should be a one liner I guess?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line will be longer than 80 characters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know but as far as I know we don't wrap lines that exceeds 80 characters (even 120 characters).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok changed.

@Tobion
Copy link
Contributor

Tobion commented Dec 26, 2014

What is the intention with the serializer component? I think we should not just blindly rebuild all the features that the JMS serializer already has. IMO both serializers should join forces and make one good one that is also faster than the current JMS serializer. There is no point in just reinventing the wheel here.

@dunglas
Copy link
Member Author

dunglas commented Dec 27, 2014

Hi @Tobion,

My intention is not to blindly copy/reinvent JMS Serializer. I’ve already explained the need here: #12092 (comment)

To summarize, I think we need:

  • a fully featured MIT (so GPLv2 compliant) serializer (this is not the case of JMSSerializer)
  • to be able to develop powerful REST (and non-REST) APIs only with Symfony, without finding and installing a ton of external dependencies
  • something really extensible and high performance

The Symfony Serializer Compnent already has something similar to this feature (it’s why I deprecated it) but it has several drawbacks:

  • it handles only underscore -> CamelCase but is not flexible enough to handle other cases
  • it’s not symmetrical: it converts when deserializing but not when serializing
  • it’s not convenient to use: you must specify the full list of properties you want to convert but you cannot say « convert all properties » (see the ticket linked in the PR).

In fact, the Serializer Component will be lighter and more efficient by default with the Name Converter System than with the current implementation because it will not be enabled/registered without manual setting (so not loaded in memory).

I think converting property names when serializing is a basic feature wanted by many people using a serializer. I've added a doc explaining some of these very common use cases: https://github.com/symfony/symfony-docs/pull/4692/files?diff=split

Naming Strategies of JMSSerializer are similar but not as powerful. AFAIK the JMS implementation as the inverse drawback than the current Serializer Component implementation: it can rename attributes while serializing but not when deserializing.

They are a lot of features I don’t want to see in the Serializer Component because they are redundant or will decrease its speed including:

  • Different ways to pick attributes to serialize (@Exclude, @Expose and so on): groups are sufficient and only one way to do seems to me better design and easier to use and understand
  • API versioning: can be handled and is similar to serialization groups
  • Annotations (or XML/YAML) for everything such as serialization names: a custom Name Converter is sufficient for such special needs
  • Event system (do we really need events for something like serialization?)

However, JMSSerializer is a very powerful and well thinked library. I think we can study how it works and the experience with it to build something lighter but as powerful.
We cannot share code between the two projects because of the licensing issue but we can share good ideas and design.
When reimplementing concepts existing in other libraries such as JMSSerializer or the Django Serializer, I try to go one step further without bloating the code. Check for instance the depth and callback system for the circular reference handler and the symmetry support in this one.

One other thing I would like to see in the Symfony component is a more generic way than the callbacks system to handle special serialization (e.g.: being able to do something when encountering a special type like \DateTime).

@Tobion
Copy link
Contributor

Tobion commented Dec 28, 2014

Thanks for the explanation. I agree having a more lightweight, MIT serializer that covers the JMS use-cases would be good.

AFAIK the JMS implementation as the inverse drawback than the current Serializer Component implementation: it can rename attributes while serializing but not when deserializing.

No, it works both ways.

@dunglas
Copy link
Member Author

dunglas commented Jan 4, 2015

Should be merged only after #13252 (and will need a rebase).

@fabpot
Copy link
Member

fabpot commented Jan 7, 2015

@dunglas #13252 is merged now, can you rebase?

@dunglas dunglas force-pushed the name_converter branch 4 times, most recently from 00f5efb to e4f34a7 Compare January 9, 2015 09:23
@dunglas
Copy link
Member Author

dunglas commented Jan 9, 2015

@fabpot rebased.

* @param string $attributeName
* @return string
*/
protected function formatAttribute($attributeName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a BC break.

@dunglas
Copy link
Member Author

dunglas commented Jan 11, 2015

@fabpot BC break reverted.

return ('.' === $match[1] ? '_' : '').strtoupper($match[2]);
}, $attributeName);
}
trigger_error('The formatAttribute() method has been deprecated. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', E_USER_DEPRECATED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using __METHOD__ would be better than using formatAttribute() (otherwise you don't know the method's class).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dunglas
Copy link
Member Author

dunglas commented Jan 12, 2015

@xabbuh fixed

@xabbuh
Copy link
Member

xabbuh commented Jan 12, 2015

@dunglas You should use the actual version numbers instead of 2.X and 2.Y. ;)

@dunglas
Copy link
Member Author

dunglas commented Jan 12, 2015

Oops :)

@dunglas
Copy link
Member Author

dunglas commented Jan 12, 2015

fixed

@fabpot
Copy link
Member

fabpot commented Jan 16, 2015

👍

}, $camelizedAttribute));
}

$this->nameConverter = new CamelCaseToSnakeCaseNameConverter($attributes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will break if a NameConverter was passed in the constructor (you will removed the existing one entirely)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended.
As NameConverter deprecate this method, both must not be used together. I'll add the throwing of an exception in that case (I thought it was done, but this is not the case).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exception added.

@stof
Copy link
Member

stof commented Jan 18, 2015

Please rebase this to fix conflicts (the logic common to the GetSetNormalizer and the PropertyNormalizer has been moved to their common parent class)

@dunglas
Copy link
Member Author

dunglas commented Jan 18, 2015

Rebased. Thanks @stof for the review.

@dunglas
Copy link
Member Author

dunglas commented Jan 23, 2015

Tests fixed.

@fabpot
Copy link
Member

fabpot commented Jan 25, 2015

@dunglas Looks like the tests are not fixed (at least, according to Travis)

@dunglas
Copy link
Member Author

dunglas commented Jan 25, 2015

@fabpot strange Travis issue. Commits squashed, now Travis has detected the changes.

@fabpot
Copy link
Member

fabpot commented Jan 26, 2015

@dunglas Tests pass now on Travis as well apparently.

@stof
Copy link
Member

stof commented Jan 30, 2015

The changelog of the component should be updated to mention the changes being done (adding the new feature and deprecating the old one). Otherwise 👍

@dunglas
Copy link
Member Author

dunglas commented Feb 2, 2015

Updated the changelog with new features of 2.6. I've also added a new feature of 2.6 missing from the changelog. Should I create a separate PR on the 2.6 branch for that?

2.6.0
-----

* added a new serializer: `PropertyNormalizer`. Like `GetSetMethodNormalizer`,
this normalizer will map an object's properties to an array.
* added circular references handling for `GetSetMethodNormalizer`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, you should submit a PR on 2.6 for this one.

@fabpot
Copy link
Member

fabpot commented Feb 3, 2015

Thank you @dunglas.

@fabpot fabpot merged commit 86b84a5 into symfony:2.7 Feb 3, 2015
fabpot added a commit that referenced this pull request Feb 3, 2015
This PR was merged into the 2.7 branch.

Discussion
----------

[Serializer] Name converter support

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #12212
| License       | MIT
| Doc PR        | symfony/symfony-docs#4692

This PR adds support for custom property naming strategies to the serializer and provides a built-in NameConverter using this new system: (CamelCase to underscore).
It handles normalization and denormalization (convert `fooBar` to `foo_bar` when serializing, then from `foo_bar` to `fooBar` when deserializing). It also has a flag to convert only some attributes.

The `setCamelizedAttributes()` is deprecated in favor of this new method (more flexible, allows to rename all attributes of a class and support deserialization) and now uses it internally.

Commits
-------

86b84a5 [Serializer] Update changelog
e14854f [Serializer] Name converter support
dunglas added a commit that referenced this pull request Mar 6, 2015
This PR was merged into the 2.7 branch.

Discussion
----------

[Serializer] ObjectNormalizer

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| License       | MIT
| Doc PR       | not yet

`PropertyAccessNormalizer` is a new normalizer leveraging the PropertyAccess Component. It is able to handle classes containing both public properties and properties only accessibles trough getters / setters / issers / hassers...

As it extends `AbstractNormalizer`, it supports circular reference handling, name converters and existing object population.
What do you think about making this new normalizer the default one as it's the most convenient to use and the most consistent with the behavior of other components.

#13120, #13252 and #13255 need to be merged to make this PR working.

Commits
-------

0050bbb [Serializer] Introduce ObjectNormalizer
weaverryan added a commit to symfony/symfony-docs that referenced this pull request Mar 13, 2015
This PR was merged into the 2.7 branch.

Discussion
----------

[Serializer] Name Converter

| Q                | A
| --------------- | ---
| Doc fix?      | no
| New docs? | yes symfony/symfony#13120
| Applies to   | 2.7

This is the documentation for the Serializer Name Converter feature.

Commits
-------

d335005 [Serializer] Fix CS
0442752 [Serializer] Acme things
57e6c94 [Serializer] Name Converter fixes
24d0320 [Serializer] Use firtName and snake_case
4ecc706 [Serializer] Name Converter corrections.
b59b752 [Serializer] Name Converter
@dunglas dunglas deleted the name_converter branch December 5, 2015 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants