Let us start with sharing working and pastable code (requires google gson package):
package mypackage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
public final class ALL {
static final Gson GSON = new GsonBuilder().registerTypeAdapterFactory(new Factory()).create();
/////////////////////////////////////////////////////////////////////
@Target( { METHOD, FIELD, ANNOTATION_TYPE, TYPE })
@Retention(RUNTIME)
public @interface Serialize {}
/////////////////////////////////////////////////////////////////////
public static void main(String[] args) {
Test test = new Test();
String json = GSON.toJson(test);
System.out.println(json);
}
/////////////////////////////////////////////////////////////////////
public static final class Test {
@Serialize
String abc = "def";
}
/////////////////////////////////////////////////////////////////////
public static final class Factory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Serialize annotation = type.getRawType().getAnnotation(Serialize.class);
boolean annotationPresent = type.getRawType().isAnnotationPresent(Serialize.class);
Annotation[] annotations = type.getRawType().getAnnotations();
if (annotationPresent) {
System.out.println("11111111111111");
}
if (annotation != null) {
return new Adapter<>();
}
return gson.getDelegateAdapter(this, type);
}
}
/////////////////////////////////////////////////////////////////////
public static final class Adapter<T> extends TypeAdapter<T> {
private static final java.util.Base64.Encoder ENCODER = java.util.Base64.getEncoder();
private static final java.util.Base64.Decoder DECODER = java.util.Base64.getDecoder();
@Override
public T read(JsonReader in) throws IOException {
in.beginObject();
String a = in.nextString();
in.endObject();
try {
return deserialize( DECODER.decode(a) );
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public void write(JsonWriter out, T value) throws IOException {
out.value( encode(serialize(value)) );
}
private String encode(byte[] serialize) {
return ENCODER.encodeToString( serialize );
}
private byte[] serialize(T value) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(out); ) {
os.writeObject(value);
return out.toByteArray();
}
}
private T deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream in = new ByteArrayInputStream(bytes); ObjectInputStream is = new ObjectInputStream(in); ) {
return (T) is.readObject();
}
}
}
}
If we look at Test class, the goal is to output something else if the annotation @Serialize is present. In this case we output bytes in String. And when we then read this back, we would like to deserialize it.
Other ways of understanding the goal is to think of maybe using an annotation you would like to encrypt a value and you could decrypt it on readback.
This should be possible, no?
I know i can register TypeAdapters based on field type, however, I would like to be able to use annotations to declare intent instead.
No wrapper classes. You can create a custom JsonSerializer but this requires registering.
In the example above, the type.getRawType().getAnnotation(Serialize.class); is always returning null and Annotation[] annotations = type.getRawType().getAnnotations() always empty, so unable to detect using the factory.
Unsure how to detect the annotation dynamically.
Do you know?