Skip to content
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

Add TypeAdapter methods for custom property name conversion #1961

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Marcono1234
Copy link
Collaborator

@Marcono1234 Marcono1234 commented Sep 12, 2021

Resolves #127
Relates to / resolves #1722
Supersedes #1035

Currently Gson uses String.valueOf for Map key serialization and the regular TypeAdapter.read method for deserialization. This is often not symmetric, preventing serialized data from being deserialized again, and is also not easily customizable by the user.

This pull request adds the TypeAdapter methods createPropertyName(T) and readFromPropertyName(String). These allow the user to define how values should be converted to a property name and how it should be converted back. The default implementation of using String.valueOf was kept for backward compatibility.
These methods have the following advantages:

  • Users can customize how conversion to property names should happen; and for classes where this makes no sense can also prevent it by throwing an exception
  • Custom type adapter factories for Map-like types can uses these methods as well, having a well defined way to convert Map keys
  • For these cases where Gson is currently not behaving well for Map keys (e.g. Deserisation fails with when using null numeric primitives as map keys #1385 or Gson Serializetion Bug in Enum Map #1920) users can at least provide their own type adapter which properly performs the conversion delegating to the existing adapter. An example for this can be seen in the added test method MapTest.testCustomEnumMapKeyConversion().

It might have been ideal if Gson added such methods in the past already so only types for which conversion to property names makes sense would have opted-in into the conversion. The current opt-out situation (especially with String.valueOf usage) is error-prone because users convert values to property names for which this was never intended or considered.

The implementation of this pull request allowed removing the error-prone promoteNameToValue method (see also #1768). This comes at the cost that the default implementation of readFromPropertyName creates a new JsonTreeReader wrapping the name as JsonPrimitive. This is not as performant, loses information about whether the original JsonReader was lenient and about the JSON path. The lenient setting is probably not that important because it would have only been used when the name was read as non-finite double. The missing JSON path can be worked around by having the caller catch the exceptions and add additional context, such as the JSON path. In general it is recommended that adapters which do want to support property name conversion should override readFromPropertyName for better performance.
An alternative would have been to use the signature readFromPropertyName(JsonReader); this would solve these issues and would also allow streaming reading of the name (as proposed by #1604; though most likely rarely / never needed), but would require keeping promoteNameToValue and I think it would reduce usability because readFromPropertyName will only be called for names, so only nextName() (and a few other methods) can be called; all other ones would always throw an IllegalStateException due to token mismatch. The approach of only passing a String to the method seems less error-prone and easier to use to me.

Additionally some of the adapters implemented in TypeAdapters.java have been
adjusted to override readFromPropertyName and createPropertyName to avoid the
indirection over JsonTreeReader for better performance. Their implementation
should be (nearly) the same as the previous implementation, even if that was
not ideal.
@google-cla google-cla bot added the cla: yes label Sep 12, 2021
Comment on lines +321 to +322
// TODO: Throwing UnsupportedOperationException is not ideal because it 'bubbles up'
// too far the call stack, but Gson has no better fitting exception
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure about this one

Comment on lines +353 to +355
// TODO: Throwing UnsupportedOperationException is not ideal because it 'bubbles up'
// too far the call stack, but Gson has no better fitting exception which also
// fits for createPropertyName
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure about this one

@Marcono1234 Marcono1234 marked this pull request as draft September 18, 2021 01:04
@Override public String getPath() {
// Does not have access to actual path, so at least make it obvious that
// path is unknown
return "#fake-property-name-path";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This path and especially the "fake" might be a bit irritating for users; maybe just use <property-name> or similar as path? That hopefully should make it obvious enough that this is not the actual name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TypeAdapters don't apply to map keys Should support custom serializers for map keys
1 participant