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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41937303/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:48:19  来源:igfitidea点击:

Gson & Java - Attempted to serialize java.lang.Class: ..... Forgot to register a type adapter?

javagson

提问by Dennis van der Veeke

I'm trying to create an abstract class for defining configuration classes. I wish to export and import these classes from and to JSON whenever I want to. I'm trying to achieve this using Gson.

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

I'm getting an error when writing to JSON that states it:

写入 JSON 时出现错误,说明它:

can't serialize java.lang.Class - Forgot to register a type adapter?

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

My main class: https://hastebin.com/pogohodovi.scala
Abstract config class: https://hastebin.com/adeyawubuy.cs

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

An example of a child class:

一个子类的例子:

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

}

Please note: I need to keep the variables in the child configuration classes static. I tried to create some adapters/serializers, but they don't seem to work.

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

采纳答案by Lyubomyr Shaydariv

You're probably doing:

你可能正在做:

gson.toJson(DyescapeCOREConfiguration.class)

In order to serialize this class, you still must create an instance of DyescapeCOREConfiguration. Since statics are not (de)serialized by default, you have to enablethem (IMHO, enabling such modifier is really not a good idea):

为了序列化这个类,你仍然必须创建一个DyescapeCOREConfiguration. 由于statics 默认情况下没有(反)序列化,因此您必须启用它们(恕我直言,启用此类修饰符确实不是一个好主意):

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

The output:

输出:

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

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



Update

更新

If obtaining an instance is not possible for whatever reason, write a custom Class<?>type adapter (I would never use it in practice):

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

StaticTypeAdapterFactory.java

静态类型适配器工厂.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

静态类型适配器.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);
        }
    }

}

Example use:

使用示例:

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

Output:

输出:

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

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

However, I still recommend you to avoid the idea of static fields (de)serialization.

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