Gson & Java - 尝试序列化 java.lang.Class: .....忘记注册类型适配器?

2022-09-03 07:45:32

我正在尝试创建一个用于定义配置类的抽象类。我希望随时将这些类从 JSON 导出和导入到 JSON。我试图使用Gson来实现这一点。

我在写入 JSON 时收到一个错误,指出它:

无法序列化 java.lang.Class - 忘记注册类型适配器?

我的主类:https://hastebin.com/pogohodovi.scala
抽象配置类:https://hastebin.com/adeyawubuy.cs

子类的示例:

public class DyescapeCOREConfiguration extends DyescapeConfiguration {

    private static transient DyescapeCOREConfiguration i = new DyescapeCOREConfiguration();
    public static DyescapeCOREConfiguration get() { return i; }

    @Expose public static String ServerID = UUID.randomUUID().toString();

}

请注意:我需要将子配置类中的变量保持静态。我试图创建一些适配器/序列化程序,但它们似乎不起作用。


答案 1

你可能正在做:

gson.toJson(DyescapeCOREConfiguration.class)

为了序列化此类,您仍然必须创建 一个 实例。由于默认情况下不会(取消)序列化,因此您必须启用它们(恕我直言,启用此类修饰符确实不是一个好主意):DyescapeCOREConfigurationstatic

    final Gson gson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .excludeFieldsWithModifiers(TRANSIENT) // STATIC|TRANSIENT in the default configuration
            .create();
    final String json = gson.toJson(new DyescapeCOREConfiguration());
    System.out.println(json);

输出:

{“ServerID”:“37145480-64b9-4beb-b031-2d619f14a44b”}


更新

如果由于某种原因无法获得实例,请编写自定义类型适配器(我在实践中永远不会使用它):Class<?>

StaticTypeAdapterFactory.java

final class StaticTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeAdapterFactory staticTypeAdapterFactory = new StaticTypeAdapterFactory();

    private StaticTypeAdapterFactory() {
    }

    static TypeAdapterFactory getStaticTypeAdapterFactory() {
        return staticTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        final Type type = typeToken.getType();
        if ( type.equals(Class.class) ) {
            @SuppressWarnings("unchecked")
            final TypeAdapter<T> castStaticTypeAdapter = (TypeAdapter<T>) getStaticTypeAdapter(gson);
            return castStaticTypeAdapter;
        }
        return null;
    }

}

StaticTypeAdapter.java

final class StaticTypeAdapter<T>
        extends TypeAdapter<Class<T>> {

    private static final String TARGET_CLASS_PROPERTY = "___class";

    private final Gson gson;

    private StaticTypeAdapter(final Gson gson) {
        this.gson = gson;
    }

    static <T> TypeAdapter<Class<T>> getStaticTypeAdapter(final Gson gson) {
        return new StaticTypeAdapter<>(gson);
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final Class<T> value)
            throws IOException {
        try {
            final Iterator<Field> iterator = Stream.of(value.getFields())
                    .filter(f -> isStatic(f.getModifiers()))
                    .iterator();
            out.beginObject();
            while ( iterator.hasNext() ) {
                final Field field = iterator.next();
                out.name(field.getName());
                field.setAccessible(true);
                final Object fieldValue = field.get(null);
                @SuppressWarnings({ "unchecked", "rawtypes" })
                final TypeAdapter<Object> adapter = (TypeAdapter) gson.getAdapter(field.getType());
                adapter.write(out, fieldValue);
            }
            out.name(TARGET_CLASS_PROPERTY);
            out.value(value.getName());
            out.endObject();
        } catch ( final IllegalAccessException ex ) {
            throw new IOException(ex);
        }
    }

    @Override
    public Class<T> read(final JsonReader in)
            throws IOException {
        try {
            Class<?> type = null;
            in.beginObject();
            final Map<String, JsonElement> buffer = new HashMap<>();
            while ( in.peek() != END_OBJECT ) {
                final String property = in.nextName();
                switch ( property ) {
                case TARGET_CLASS_PROPERTY:
                    type = Class.forName(in.nextString());
                    break;
                default:
                    // buffer until the target class name is known
                    if ( type == null ) {
                        final TypeAdapter<JsonElement> adapter = gson.getAdapter(JsonElement.class);
                        final JsonElement jsonElement = adapter.read(in);
                        buffer.put(property, jsonElement);
                    } else {
                        // flush the buffer
                        if ( !buffer.isEmpty() ) {
                            for ( final Entry<String, JsonElement> e : buffer.entrySet() ) {
                                final Field field = type.getField(e.getKey());
                                final Object value = gson.getAdapter(field.getType()).read(in);
                                field.set(null, value);
                            }
                            buffer.clear();
                        }
                        final Field field = type.getField(property);
                        if ( isStatic(field.getModifiers()) ) {
                            final TypeAdapter<?> adapter = gson.getAdapter(field.getType());
                            final Object value = adapter.read(in);
                            field.set(null, value);
                        }
                    }
                    break;
                }
            }
            in.endObject();
            // flush the buffer
            if ( type != null && !buffer.isEmpty() ) {
                for ( final Entry<String, JsonElement> e : buffer.entrySet() ) {
                    final Field field = type.getField(e.getKey());
                    final Object value = gson.fromJson(e.getValue(), field.getType());
                    field.set(null, value);
                }
                buffer.clear();
            }
            @SuppressWarnings({ "unchecked", "rawtypes" })
            final Class<T> castType = (Class) type;
            return castType;
        } catch ( final ClassNotFoundException | NoSuchFieldException | IllegalAccessException ex ) {
            throw new IOException(ex);
        }
    }

}

使用示例:

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getStaticTypeAdapterFactory())
        .create();
final String json = gson.toJson(DyescapeCOREConfiguration.class);
out.println("DyescapeCOREConfiguration.ServerID=" + DyescapeCOREConfiguration.ServerID);
// ---
DyescapeCOREConfiguration.ServerID = "whatever";
out.println("DyescapeCOREConfiguration.ServerID=" + DyescapeCOREConfiguration.ServerID);
// ---
@SuppressWarnings("unchecked")
final Class<DyescapeCOREConfiguration> configurationClass = gson.fromJson(json, Class.class);
//    ^--- this is awful, omitting a useless assignment is even worse
out.println("DyescapeCOREConfiguration.ServerID=" + DyescapeCOREConfiguration.ServerID);

输出:

DyescapeCOREConfiguration.ServerID=012fa795-abd8-4b91-b6f5-bab67f73ae17
DyescapeCOREConfiguration.ServerID=whatever
DyescapeCOREConfiguration.ServerID=012fa795-abd8-4b91-b6f5-bab67f73ae17

但是,我仍然建议您避免静态字段(反)序列化的想法。


答案 2

推荐