rharter / auto-value-gson

AutoValue Extension to add Gson De/Serializer support
Apache License 2.0
607 stars 103 forks source link

TypeAdapterFactory as service providers for the Java service loader #140

Closed davidepedranz closed 7 years ago

davidepedranz commented 7 years ago

Hi, first of all thanks you for this great library!

Recently I faced the following problem problem. I am working on a project that uses many libraries to interact with various services in our platform. I use auto-value whenever I can and gson together with auto-value-gson for serialisation / deserialisation. I would like to be able to automatically register all classes annotated with @AutoValue to my gson instance, even if they come from an external library.

I would like a way to automatically register to gson all TypeAdapterFactory present in the libraries. I think a solution could be to rely on the Java service loaders, something like this:

final GsonBuilder gsonBuilder = new GsonBuilder()

// register custom type adapter factories
for (TypeAdapterFactory factory : ServiceLoader.load(TypeAdapterFactory.class)) {
    gsonBuilder.registerTypeAdapterFactory(factory);
}

final Gson gson = gsonBuilder.create();

I tried to register the abstract class annotated with @GsonTypeAdapterFactory as a service provider using @AutoService, but Java fails to instantiate it since abstract. I propose it to optionally annotate the generated TypeAdapterFactory with @AutoService, so that one can use the Java service provider to dynamically load all factories when needed.

I though about a parameter to the @GsonTypeAdapterFactory to optionally enable this feature (disabled by default). So this class:

@GsonTypeAdapterFactory(autoservice = true)
public abstract class SampleAdapterFactory implements TypeAdapterFactory {
    public static SampleAdapterFactory create() {
        return new AutoValueGson_SampleAdapterFactory();
    }
}

will generate this implementation:

@AutoService(TypeAdapterFactory.class)
public final class AutoValueGson_SampleAdapterFactory extends SampleAdapterFactory {
  @Override
  @SuppressWarnings("unchecked")
  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    Class<T> rawType = (Class<T>) type.getRawType();
    if (Address.class.isAssignableFrom(rawType)) {
      return (TypeAdapter<T>) Address.typeAdapter(gson);
    } else {
      return null;
    }
  }
}

What do you think? If you like the idea, I would be happy to make a pull-request to implement this feature.

ZacSweers commented 7 years ago

I think the idea is interesting, but my gut says this library either needs a proper extensions API or it shouldn't do this. Kind of just bolting on features without much direction

davidepedranz commented 7 years ago

Do you plan to provide an extension API?

rharter commented 7 years ago

I think an extension API to an extension is a bit of extensionception.

Could this be accomplished with a very simple delegating wrapper?

@AutoService(TypeAdapterFactory.class)public final class DelegatingTypeAdapterFactory implements TypeAdapterFactory {

private final SampleAdapterFactory delegate = SampleAdapterFactory.create();

@Override @SuppressWarnings("unchecked") public TypeAdapter create(Gson gson, TypeToken type) { return delegate.create(gson, type); } }

I understand that's a little extra work, but something like this could be generated with a separate annotation processor that simply processes the @GsonTypeAdapterFactory annotation in addition to the current one.

On Wed, Aug 2, 2017 at 6:21 AM Davide Pedranz notifications@github.com wrote:

Do you plan to provide an extension API?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rharter/auto-value-gson/issues/140#issuecomment-319644055, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPJbrL_k2QGqz9VzPiCzZfgHFQp07TUks5sUFutgaJpZM4Okdtj .

davidepedranz commented 7 years ago

Thank you for the suggestion @rharter, I find it a very clean and elegant solution!

I will try to implement such an annotation processor. Let me know if you may be interested in adding it to this library in the future.