Annotation Interface如何转为JSON?

How to convert Annotation Interface to JSON?

我正在尝试读取通过不同类加载器加载的自定义注释值。

如何将 Annotation 对象转换为 json?

@Retention(RententionPolicy.RUNTIME)
public @interface SendEmail{
public String id;
}

Gson gson = new Gson();

Object object = (Object)annotation // this holds SendEmail object loaded from different classloader

gson.toJson(object); 

//I get UnsupportedOperationException: Attempted to Serialize java.lang.Class: SendEmail. Forget to register a type adapter?

用于接口的类型适配器是什么?

我不太确定为什么需要序列化设计为常量的注解实例,但您很可能使用的是使用 java.lang.proxy.Proxy 实例化注解的 Oracle JVM。代理在 Gson 中被视为反射访问数据对象(并且在标准 Gson 包中没有提到代理)并且 Gson 在序列化 java.lang.Class 时失败,这在您的场景中没有多大意义。

您需要创建一个新的类型适配器工厂,它可以发出一个注释感知类型适配器,该适配器将大量使用对注释的反射。说,

final class AnnotationTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeAdapterFactory instance = new AnnotationTypeAdapterFactory();

    private AnnotationTypeAdapterFactory() {
    }

    static TypeAdapterFactory getInstance() {
        return instance;
    }

    @Override
    @Nullable
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        @Nullable
        final Class<? extends Annotation> annotationClass = Annotations.lookupAnnotationClass(typeToken.getRawType());
        if ( annotationClass == null ) {
            return null;
        }
        final List<Method> methods = Annotations.lookupMethods(annotationClass);
        final int count = methods.size();
        final String[] names = new String[count];
        @SuppressWarnings("unchecked")
        final TypeAdapter<Object>[] typeAdapters = new TypeAdapter[count];
        final Map<String, TypeAdapter<Object>> namedTypeAdapters = new HashMap<>();
        for ( int i = 0; i < count; i++ ) {
            final Method method = methods.get(i);
            names[i] = method.getName();
            @SuppressWarnings({ "unchecked", "rawtypes" })
            final TypeAdapter<Object> typeAdapter = (TypeAdapter) gson.getAdapter(method.getReturnType());
            typeAdapters[i] = typeAdapter;
            namedTypeAdapters.put(names[i], typeAdapter);
        }
        final TypeAdapter<T> typeAdapter = new TypeAdapter<T>() {
            @Override
            @SuppressWarnings("resource")
            public void write(final JsonWriter out, final T annotation)
                    throws IOException {
                try {
                    out.beginObject();
                    for ( int i = 0; i < count; i++ ) {
                        out.name(names[i]);
                        typeAdapters[i].write(out, methods.get(i).invoke(annotation));
                    }
                    out.endObject();
                } catch ( final IllegalAccessException | InvocationTargetException ex ) {
                    throw new RuntimeException(ex);
                }
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                try {
                    in.beginObject();
                    final Map<String, Object> properties = new HashMap<>();
                    while ( in.hasNext() ) {
                        final String name = in.nextName();
                        @Nullable
                        final TypeAdapter<Object> objectTypeAdapter = namedTypeAdapters.get(name);
                        if ( objectTypeAdapter == null ) {
                            in.skipValue();
                        } else {
                            properties.put(name, objectTypeAdapter.read(in));
                        }
                    }
                    in.endObject();
                    @SuppressWarnings("unchecked")
                    final T annotation = (T) Annotations.create(annotationClass, properties);
                    return annotation;
                } catch ( final NoSuchMethodException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        };
        return typeAdapter.nullSafe();
    }

}

其中Annotations class如下:

final class Annotations {

    private static final boolean SUN_PACKAGE = false;

    private Annotations() {
    }

    static <T extends Annotation> T create(final Class<T> annotationClass, final Map<String, Object> properties)
            throws NoSuchMethodException {
        return create(annotationClass.getClassLoader(), annotationClass, properties);
    }

    static <T extends Annotation> T create(final ClassLoader classLoader, final Class<T> annotationClass, final Map<String, Object> properties)
            throws NoSuchMethodException {
        if ( SUN_PACKAGE ) {
            @SuppressWarnings("unchecked")
            final T annotation = (T) AnnotationParser.annotationForMap(annotationClass, properties);
            return annotation;
        }
        @SuppressWarnings("unchecked")
        final T annotation = (T) Proxy.newProxyInstance(
                classLoader,
                new Class<?>[]{ annotationClass },
                DynamicAnnotation.fromMap(annotationClass, lookupProperties(annotationClass, properties))
        );
        return annotation;
    }

    @Nullable
    static Class<? extends Annotation> lookupAnnotationClass(final Class<?> clazz) {
        if ( clazz.isAnnotation() ) {
            @SuppressWarnings("unchecked")
            final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz;
            return annotationClass;
        }
        final Class<?>[] interfaces = clazz.getInterfaces();
        if ( interfaces.length != 1 ) {
            return null;
        }
        final Class<?> iface = interfaces[0];
        if ( !Annotation.class.isAssignableFrom(iface) ) {
            return null;
        }
        @SuppressWarnings("unchecked")
        final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) iface;
        return annotationClass;
    }

    static List<Method> lookupMethods(final Class<? extends Annotation> annotationClass) {
        final List<Method> methods = new ArrayList<>();
        for ( final Method method : annotationClass.getMethods() ) {
            if ( method.getDeclaringClass() == annotationClass ) {
                methods.add(method);
            }
        }
        return Collections.unmodifiableList(methods);
    }

    static Map<String, Object> lookupProperties(final Class<? extends Annotation> annotationClass, final Map<String, Object> properties) {
        final Map<String, Object> namedProperties = new HashMap<>();
        namedProperties.putAll(lookupDefaultProperties(annotationClass));
        namedProperties.putAll(properties);
        return Collections.unmodifiableMap(namedProperties);
    }

    static Map<String, Object> lookupDefaultProperties(final Class<? extends Annotation> annotationClass) {
        final Map<String, Object> defaultProperties = new HashMap<>();
        for ( final Method method : lookupMethods(annotationClass) ) {
            @Nullable
            final Object defaultValue = method.getDefaultValue();
            if ( defaultValue != null ) {
                defaultProperties.put(method.getName(), defaultValue);
            }
        }
        return Collections.unmodifiableMap(defaultProperties);
    }

    static String toString(@SuppressWarnings("TypeMayBeWeakened") final Class<? extends Annotation> annotationClass, final Map<String, Object> properties) {
        final StringBuilder builder = new StringBuilder("@")
                .append(annotationClass.getTypeName())
                .append('(');
        boolean atTail = false;
        for ( final Map.Entry<String, Object> e : properties.entrySet() ) {
            if ( atTail ) {
                builder.append(", ");
            }
            builder.append(e.getKey())
                    .append('=')
                    .append(e.getValue());
            atTail = true;
        }
        return builder.append(')')
                .toString();
    }

}

和注释的自定义实现:

abstract class DynamicAnnotation
        implements Annotation, InvocationHandler {

    private static final Method java_lang_Object_equals;
    private static final Method java_lang_Object_hashCode;
    private static final Method java_lang_Object_toString;
    private static final Method java_lang_annotation_Annotation_annotationType;

    static {
        try {
            java_lang_Object_equals = Object.class.getDeclaredMethod("equals", Object.class);
            java_lang_Object_hashCode = Object.class.getDeclaredMethod("hashCode");
            java_lang_Object_toString = Object.class.getDeclaredMethod("toString");
            java_lang_annotation_Annotation_annotationType = Annotation.class.getDeclaredMethod("annotationType");
        } catch ( final NoSuchMethodException ex ) {
            throw new Error(ex);
        }
    }

    private final String toString;
    private final Class<? extends Annotation> annotationClass;

    private DynamicAnnotation(final String toString, final Class<? extends Annotation> annotationClass) {
        this.toString = toString;
        this.annotationClass = annotationClass;
    }

    static DynamicAnnotation fromMap(final Class<? extends Annotation> annotationClass, final Map<String, Object> properties)
            throws NoSuchMethodException {
        return FromMap.create(annotationClass, properties);
    }

    @Nullable
    protected abstract Object invoke(final Method method)
            throws Throwable;

    @Override
    public final Class<? extends Annotation> annotationType() {
        return annotationClass;
    }

    // must conform the https://docs.oracle.com/javase/6/docs/api/java/lang/annotation/Annotation.html#hashCode() contract
    @Override
    public final int hashCode() {
        //return hashCode;
        throw new UnsupportedOperationException();
    }

    // must conform the https://docs.oracle.com/javase/6/docs/api/java/lang/annotation/Annotation.html#equals(java.lang.Object) contract
    @Override
    public final boolean equals(@Nullable final Object obj) {
        throw new UnsupportedOperationException();
    }

    @Override
    public final String toString() {
        return toString;
    }

    @Override
    @Nonnull
    public final Object invoke(final Object proxy, final Method method, final Object[] args)
            throws Throwable {
        if ( method.equals(java_lang_annotation_Annotation_annotationType) ) {
            return annotationType();
        }
        if ( method.equals(java_lang_Object_equals) ) {
            return equals(args[0]);
        }
        if ( method.equals(java_lang_Object_hashCode) ) {
            return hashCode();
        }
        if ( method.equals(java_lang_Object_toString) ) {
            return toString();
        }
        @Nullable
        final Object returnValue = invoke(method);
        if ( returnValue == null ) {
            throw new NoSuchMethodException("The instance of " + annotationClass + " has no value associated with " + method.getName());
        }
        return returnValue;
    }

    private static final class FromMap
            extends DynamicAnnotation {

        private final Map<String, Object> properties;

        private FromMap(final String toString, final Class<? extends Annotation> annotationClass, final Map<String, Object> properties) {
            super(/*hashCode, */toString, annotationClass);
            this.properties = properties;
        }

        private static DynamicAnnotation create(final Class<? extends Annotation> annotationClass, final Map<String, Object> properties)
                throws NoSuchMethodException {
            final Map<String, Object> toStringProperties = new LinkedHashMap<>();
            for ( final Method method : Annotations.lookupMethods(annotationClass) ) {
                final String name = method.getName();
                if ( !properties.containsKey(name) ) {
                    throw new NoSuchMethodException("Cannot find " + name + " in " + properties + " while constructing an instance of " + annotationClass);
                }
                final Object value = properties.get(name);
                toStringProperties.put(name, value);
            }
            final String toString = Annotations.toString(annotationClass, Collections.unmodifiableMap(toStringProperties));
            return new FromMap(toString, annotationClass, properties);
        }

        @Override
        protected Object invoke(final Method method) {
            return properties.get(method.getName());
        }

    }

}

您也可以使用 AnnotationUtils(如果它适合您),或 "sun package" 中的 AnnotationParser(如果它是一个选项)来实现注释接口契约。

这是往返的使用示例:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(AnnotationTypeAdapterFactory.getInstance())
        .create();

public static void main(final String... args)
        throws NoSuchFieldException {
    final SendEmail before = Wrapper.class.getDeclaredField("constant").getAnnotation(SendEmail.class);
    System.out.println(before);
    final String json = gson.toJson(before);
    System.out.println(json);
    final SendEmail after = gson.fromJson(json, SendEmail.class);
    System.out.println(after);
    System.out.println(after.annotationType());
    System.out.println(gson.toJson(after));
}

private static final class Wrapper {

    @SendEmail(id = "email@mail.com")
    private static final Object constant = new Object();

}