java—为google gson添加自定义typeadapterfactory,以检测注解的使用情况,从而动态返回自定义typeadapter

7bsow1i6  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(605)

让我们从共享工作和可复制的代码开始(需要google gson包):

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();
            }
        }
    }
}

如果我们看看 Test 类,目标是在注解 @Serialize 存在。在本例中,我们以字符串形式输出字节。当我们读回这个时,我们想反序列化它。
理解这个目标的另一种方法是考虑使用一个注解来加密一个值,然后在readback上解密它。
这应该是可能的,不是吗?
我知道我可以基于字段类型注册typeadapters,但是,我希望能够使用注解来声明意图。
没有 Package 类。您可以创建自定义jsonserializer,但这需要注册。
在上面的例子中 type.getRawType().getAnnotation(Serialize.class); 总是返回null和 Annotation[] annotations = type.getRawType().getAnnotations() 总是空的,所以无法检测使用工厂。
不确定如何动态检测注解。
你知道吗?

avkwfej4

avkwfej41#

使用@jsonadapter怎么样?无论如何,您需要知道如何进行解密,并且需要实现每个类型的tha。例如,对于本例中的字符串:

public class CryptoAdapter extends TypeAdapter<String> {
    @Override
    public void write(JsonWriter out, String value) throws IOException {
        out.jsonValue(org.apache.commons.lang3.StringUtils.reverse(value));
    }
    @Override
    public String read(JsonReader in) throws IOException {
        return org.apache.commons.lang3.StringUtils.reverse(in.nextString());
    }
}

用法:

public class Test {
    @JsonAdapter(CryptoAdapter.class)
    String abc = "def";
}

问题是,据我所知,gson没有提供任何直接的方法来创建一些自己的字段处理器,使用户可以读取字段/类成员注解。
换句话说,您需要在反序列化/序列化期间访问该字段,这似乎不可能以简单的方式实现。
所以才有这个 @JsonAdapter .
如果有兴趣从github学习更多克隆源代码,请检查:

public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory

不幸的是 final . 有一个名为 createBoundField (我认为这就是 @JsonAdapter 对于字段),路径和重写该逻辑并不是那么简单。
对于类,似乎有与您的解决方案非常相似的解决方案:

public final class JsonAdapterAnnotationTypeAdapterFactory
    implements TypeAdapterFactory

上述两项均已列入 TypeAdapterFactories 当一个新的 Gson 已创建。

相关问题