-
Star
(206)
You must be signed in to star a gist -
Fork
(15)
You must be signed in to fork a gist
-
-
Save JakeWharton/0d67d01badcee0ae7bc9 to your computer and use it in GitHub Desktop.
| import com.google.auto.value.AutoValue; | |
| import java.lang.annotation.Retention; | |
| import java.lang.annotation.Target; | |
| import static java.lang.annotation.ElementType.TYPE; | |
| import static java.lang.annotation.RetentionPolicy.RUNTIME; | |
| /** | |
| * Marks an {@link AutoValue @AutoValue}-annotated type for proper Gson serialization. | |
| * <p> | |
| * This annotation is needed because the {@linkplain Retention retention} of {@code @AutoValue} | |
| * does not allow reflection at runtime. | |
| */ | |
| @Target(TYPE) | |
| @Retention(RUNTIME) | |
| public @interface AutoGson { | |
| } |
| import com.google.gson.Gson; | |
| import com.google.gson.TypeAdapter; | |
| import com.google.gson.TypeAdapterFactory; | |
| import com.google.gson.reflect.TypeToken; | |
| public final class AutoValueAdapterFactory implements TypeAdapterFactory { | |
| @SuppressWarnings("unchecked") | |
| @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
| Class<? super T> rawType = type.getRawType(); | |
| if (!rawType.isAnnotationPresent(AutoGson.class)) { | |
| return null; | |
| } | |
| String packageName = rawType.getPackage().getName(); | |
| String className = rawType.getName().substring(packageName.length() + 1).replace('$', '_'); | |
| String autoValueName = packageName + ".AutoValue_" + className; | |
| try { | |
| Class<?> autoValueType = Class.forName(autoValueName); | |
| return (TypeAdapter<T>) gson.getAdapter(autoValueType); | |
| } catch (ClassNotFoundException e) { | |
| throw new RuntimeException("Could not load AutoValue type " + autoValueName, e); | |
| } | |
| } | |
| } |
| import com.google.auto.value.AutoValue; | |
| import com.google.gson.Gson; | |
| import com.google.gson.GsonBuilder; | |
| public class Main { | |
| public static void main(String... args) { | |
| Gson gson = new GsonBuilder() | |
| .registerTypeAdapterFactory(new AutoValueAdapterFactory()) | |
| .create(); | |
| Test inTest = Test.of("John", "Doe", 100); | |
| System.out.println("IN: " + inTest); | |
| String json = gson.toJson(inTest); | |
| System.out.println("JSON: " + json); | |
| Test outTest = gson.fromJson(json, Test.class); | |
| System.out.println("OUT: " + outTest); | |
| } | |
| @AutoValue @AutoGson | |
| public abstract static class Test { | |
| public static Test of(String firstName, String lastName, int age) { | |
| return new AutoValue_Main_Test(firstName, lastName, age); | |
| } | |
| public abstract String firstName(); | |
| public abstract String lastName(); | |
| public abstract int age(); | |
| } | |
| } | |
If someone need some simple test for @thenuge solution
import com.google.auto.value.AutoValue;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Before;
import org.junit.Test;
import static com.google.common.truth.Truth.assert_;
public class AutoValueAdapterFactoryTest {
private Gson mGson;
@AutoValue
@AutoGson(autoValueClass = AutoValue_AutoValueAdapterFactoryTest_Some.class)
public abstract static class Some {
public static Some of(String firstName, String lastName, int age) {
return new AutoValue_AutoValueAdapterFactoryTest_Some(firstName, lastName, age);
}
public abstract String firstName();
public abstract String lastName();
public abstract int age();
}
@Before
public void setUp() throws Exception {
mGson = new GsonBuilder()
.registerTypeAdapterFactory(new AutoValueAdapterFactory())
.create();
}
@Test
public void testParsing() throws Exception {
final Some inSome = Some.of("John", "Doe", 100);
final String json = mGson.toJson(inSome);
final Some outSome = mGson.fromJson(json, Some.class);
assert_().that(outSome).isEqualTo(inSome);
}
}Rather than using a new Annotation, testing for an abstract class may be a little more automatic.
public final class AutoValueAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
// If it's an abstract class, it's likely to be an AutoValue
int m = rawType.getModifiers();
if (!Modifier.isAbstract(m)) {
return null;
}
String packageName = rawType.getPackage().getName();
String className = rawType.getName().substring(packageName.length() + 1).replace('$', '_');
String autoValueName = packageName + ".AutoValue_" + className;
try {
Class<?> autoValueType = Class.forName(autoValueName);
return (TypeAdapter<T>) gson.getAdapter(autoValueType);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load AutoValue type " + autoValueName, e);
}
}
}Hi!
I'm trying to test this chunk of code and I'm always bumping into a no-args contructor problem.
Looking at the code generated by AutoValue, there is indeed no no-args constructor (as it should, afaik).
What am I missing?
Thanks!
@codedance That is far too presumptuous to work at scale.
Is there a way how to use @SerializedName with AutoValue and this TypeAdapterFactory?
@semanticer https://github.com/rharter/auto-value-gson supports @SerializedName.
Also interested in adding SerializedName.
Ok, what about Builders ? AutoValue for android support builder pattern, however when i use it with your @AutoGson it's giving me objects full of nulls. Is there any good way to go or i need to write my own solution ?
Does this work with generics?
@AutoValue @AutoGson
public abstract static class Some<T> {
public static Some <X> of(String firstName, String lastName, int age, X genericData) {
return new AutoValue_Main_Test<>(firstName, lastName, age, genericData);
}
public abstract String firstName();
public abstract String lastName();
public abstract int age();
public abstract T genericData();
}When you do something like:
public class People {
List<Some<Map<String,String>> someList;
}And try to parse a "People" json, will the generic be resolved?
@danielesegato https://github.com/rharter/auto-value-gson works with generics
Very useful, thanks! I made a slight adjustment to make it work with obfuscation, since using hardcoded strings to find the AutoValue-generated class no longer works once you start obfuscating class names. It just takes a class reference instead:
Then, you just need to decorate your AutoValue class like so (following your example):