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 support for custom actions on annotations #269

Open
GoogleCodeExporter opened this issue Mar 19, 2015 · 15 comments
Open

Add support for custom actions on annotations #269

GoogleCodeExporter opened this issue Mar 19, 2015 · 15 comments

Comments

@GoogleCodeExporter
Copy link

See the thread: 
http://groups.google.com/group/google-gson/browse_thread/thread/1ea949a5e50cc507

by supporting a custom action on an annotation, we will enable support for 
JPA/Hibernate and other kinds of annotations.

Original issue reported on code.google.com by inder123 on 26 Nov 2010 at 7:00

@inder123
Copy link
Collaborator

#930

@fwonce
Copy link

fwonce commented Feb 22, 2017

One simple example: I'm using Objectify on AppEngine, and so one of my entity
classes is like so:

public class MyThing {
@id Long id;
String title;
}

I want this to map to:
{"id":443,"title":"Thing"}

If I could write a type adapter that triggers off annotations, I could just use
the existing @id annnotation to ensure the Long is serialized as a String

Exactly the same need here. Another reason: if MyThing in the example above is located in a common package meant to provide as a 3rd party jar and will be serialized to JSON format in some application at runtime. Provided that for good reason the common jar won't include gson in its dependencies, to archive this goal, the application can register a custom TypeAdapter or TypeAdapterFactory serving the fields annotated with @Id. Simple and clean for the client code, right?

I made a commit to my fork for a quick review on the idea. @inder123 will you please review this?

fwonce pushed a commit to fwonce/gson that referenced this issue Mar 2, 2017
fwonce pushed a commit to fwonce/gson that referenced this issue Mar 2, 2017
@amaksoft
Copy link

amaksoft commented Jun 17, 2017

Hi, i want to share my implementation of custom annotation support for gson, which is backwards compatible:
new interface ParametrizedTypeAdapterFactory extends TypeAdapterFactory and new method
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type, Annotation[] annotations)
only gets called if field is annotated with specific annotation @Parametrized

I mostly wanted it for serializing some dates by specifying custom pattern in annotation like this:

class Person {

  @Expose
  @SerializedName("birth_date")
  @Parametrized
  @CustomDateFormat("MMMM d, yyyy h:mm:ss a z")
  private Date birthDate;

}

Here is a draft implementation:
amaksoft@b264334
Can you please review it?

@amaksoft
Copy link

Just wanted to explain my position:
ParamterizedTypeAdapterFactory is just a special case of TypeAdapterFactory. In most cases Gson don't even need to know that your factory accepts annotations. But in special cases like @CustomDateFormat on Date or @Id on int, you return a customized TypeAdapter only for this case, (we don't cache it).
No new lists of adapters needed, any of your existing TypeAdapters could be easily transformed into parametrized ones by implementing a new method.

@momomo
Copy link

momomo commented Dec 3, 2020

@amaksoft Why this is not yet accepted? Jeezus.

Related?
https://stackoverflow.com/questions/65130321/adding-a-custom-typeadapterfactory-for-google-gson-to-detect-use-of-annotations

Hi, i want to share my implementation of custom annotation support for gson, which is backwards compatible:
new interface ParametrizedTypeAdapterFactory extends TypeAdapterFactory and new method
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type, Annotation[] annotations)
only gets called if field is annotated with specific annotation @Parametrized

I mostly wanted it for serializing some dates by specifying custom pattern in annotation like this:

class Person {

  @Expose
  @SerializedName("birth_date")
  @Parametrized
  @CustomDateFormat("MMMM d, yyyy h:mm:ss a z")
  private Date birthDate;

}

Here is a draft implementation:
amaksoft@b264334
Can you please review it?

@momomo
Copy link

momomo commented Dec 3, 2020

Anyway this can be added without hacking google gson library ?

@Marcono1234
Copy link
Collaborator

Another approach could be to extend Gson's TypeToken class with a new getAnnotations() method or similar.
Below I am trying to list some advantages and disadvantages for both approaches. There might be more I didn't think of.

TypeToken containing annotations

Advantages:

  • No need for new Gson methods with additional Annotation[] parameter
  • (related to above) existing TypeAdapterFactory and Gson#getDelegateAdapter usage continues to work, and will propagate annotations

Disadvantages:

  • Not clear how TypeToken hashCode and equals should behave, and if it could be a problem if they start considering the annotations
    • For example could be a performance problem for Gson.typeTokenCache, because fields with different annotations would have separate cache entries then (even if the entry value is the same). Even worse is that this applies to annotations which might be completely irrelevant for any of the registered TypeAdapterFactory. A naive implementation would then even have separate entries just due to different @SerializedName(...) annotations on the fields, which would be rather bad.
  • For performance (and backward compatibility?) reasons, might have to make extraction of annotations opt-in with GsonBuilder method, since Gson cannot know in advance if any of the registered TypeAdapterFactory actually needs annotations for processing

Maybe could solve some of the disadvantages if either a new (default) method was added to TypeAdapterFactory which returns the annotation classes the factory is interested in, e.g. List<Class<? extends Annotation>> getRelevantAnnotationTypes(), or if the GsonBuilder method for enabling annotation extraction also specified the list of annotation classes (or a Predicate<Class<? extends Annotation>> for greater flexibility) which should be extracted. And any other annotations would then be ignored, so would hopefully not cause issues for TypeToken hashCode and equals then.

ParametrizedTypeAdapterFactory

Regarding the ParametrizedTypeAdapterFactory approach mentioned above in #269 (comment):
Advantages:

  • No issues with TypeToken hashCode and equals (since annotations will not be part of TypeToken)
  • For performance Gson might be able to inspect whether any ParametrizedTypeAdapterFactory has been registered, and if not it can skip extracting annotations
    (not sure if extracting annotations really causes noticeable overhead though)

Disadvantages:

  • Requires new API with Annotation[] parameters
  • (related to above) custom TypeAdapterFactory which wants to delegate to another factory would be cumbersome, respectively existing code would not consider annotations
    • current Gson#getDelegateAdapter does not have Annotation[] parameter (could be added though)
  • Implementation above requires separate @Parametrized annotation on fields
    (otherwise Gson cannot know when it is safe to ignore annotations and use adapter from cache, and when not?)

These are just some personal thoughts on this, and I am not a direct project member. These ideas are not specific plans to implement this.

@eamonnmcmanus
Copy link
Member

Thanks, @Marcono1234, for the detailed analysis!

I think it is only fair to say at the outset that this is probably too big a change to envisage given that we are technically in maintenance mode.

If we were going to do something, there is a further complication. This issue was originally filed in 2010, no less. In the meantime, Java 8 added type annotations in 2014. Customizing serialization and deserialization based on type annotations seems a lot more logical than customizing based on annotations that happen to be on the field that is being serialized or deserialized. The API change to support just type annotations might be as simple as adding these to TypeToken:

...
import java.lang.reflect.AnnotatedType;
...
public class TypeToken<T> {
   ...
  public static TypeToken<?> get(AnnotatedType type) {...}
  ...
  public AnnotatedType getAnnotatedType() {...}
  ...
}

But that has similar problems to the ones @Marcono1234 notes. Gson serializes and deserializes a field by extracting its type as a TypeToken and looking for a TypeAdapter for the type. If the TypeToken suddenly includes type annotations then this will be a big change in behaviour, unless type annotations are ignored when comparing two TypeToken instances for equality. If they are ignored, then the only way to handle types differently based on their annotations would be to register a TypeAdapterFactory that does look at getAnnotatedType(). Or there could be further API changes such as the ones that @Marcono1234 mentions.

(We would also need to drop Java 7 compatibility and specify a minimum of Java 8, but we've been thinking about doing that anyway. I think ten years is enough time for client code to move to a Java platform.)

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

7 participants