如何使用接口序列化类?

Ben*_*ynn 34 java json interface gson

我从未对序列化做过多少工作,但我尝试使用Google的gson将Java对象序列化为文件.这是我的问题的一个例子:

public interface Animal {
    public String getName();
}


 public class Cat implements Animal {

    private String mName = "Cat";
    private String mHabbit = "Playing with yarn";

    public String getName() {
        return mName;
    }

    public void setName(String pName) {
        mName = pName;
    }

    public String getHabbit() {
        return mHabbit;
    }

    public void setHabbit(String pHabbit) {
        mHabbit = pHabbit;
    }

}

public class Exhibit {

    private String mDescription;
    private Animal mAnimal;

    public Exhibit() {
        mDescription = "This is a public exhibit.";
    }

    public String getDescription() {
        return mDescription;
    }

    public void setDescription(String pDescription) {
        mDescription = pDescription;
    }

    public Animal getAnimal() {
        return mAnimal;
    }

    public void setAnimal(Animal pAnimal) {
        mAnimal = pAnimal;
    }

}

public class GsonTest {

public static void main(String[] argv) {
    Exhibit exhibit = new Exhibit();
    exhibit.setAnimal(new Cat());
    Gson gson = new Gson();
    String jsonString = gson.toJson(exhibit);
    System.out.println(jsonString);
    Exhibit deserializedExhibit = gson.fromJson(jsonString, Exhibit.class);
    System.out.println(deserializedExhibit);
}
}
Run Code Online (Sandbox Code Playgroud)

所以这很好地序列化 - 但可以理解的是删除了Animal上的类型信息:

{"mDescription":"This is a public exhibit.","mAnimal":{"mName":"Cat","mHabbit":"Playing with yarn"}}
Run Code Online (Sandbox Code Playgroud)

但这会导致反序列化的真正问题:

Exception in thread "main" java.lang.RuntimeException: No-args constructor for interface com.atg.lp.gson.Animal does not exist. Register an InstanceCreator with Gson for this type to fix this problem.
Run Code Online (Sandbox Code Playgroud)

我知道为什么会发生这种情况,但是我很难找到解决这个问题的正确模式.我确实看过指南,但没有直接解决这个问题.

nar*_*thi 82

这是一个通用的解决方案,适用于只有静态知道接口的所有情况.

  1. 创建序列化器/反序列化器:

    final class InterfaceAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T> {
        public JsonElement serialize(T object, Type interfaceType, JsonSerializationContext context) {
            final JsonObject wrapper = new JsonObject();
            wrapper.addProperty("type", object.getClass().getName());
            wrapper.add("data", context.serialize(object));
            return wrapper;
        }
    
        public T deserialize(JsonElement elem, Type interfaceType, JsonDeserializationContext context) throws JsonParseException {
            final JsonObject wrapper = (JsonObject) elem;
            final JsonElement typeName = get(wrapper, "type");
            final JsonElement data = get(wrapper, "data");
            final Type actualType = typeForName(typeName); 
            return context.deserialize(data, actualType);
        }
    
        private Type typeForName(final JsonElement typeElem) {
            try {
                return Class.forName(typeElem.getAsString());
            } catch (ClassNotFoundException e) {
                throw new JsonParseException(e);
            }
        }
    
        private JsonElement get(final JsonObject wrapper, String memberName) {
            final JsonElement elem = wrapper.get(memberName);
            if (elem == null) throw new JsonParseException("no '" + memberName + "' member found in what was expected to be an interface wrapper");
            return elem;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 让Gson使用它作为您选择的接口类型:

    Gson gson = new GsonBuilder().registerTypeAdapter(Animal.class, new InterfaceAdapter<Animal>())
                                 .create();
    
    Run Code Online (Sandbox Code Playgroud)

  • StackOverflow异常 - context.serialize(object)调用自身.不知道这对你有什么用. (8认同)
  • @Ryan是对的,context.serailize(object)最终会无限循环 - 对于其他人来说,它是如何工作的? (6认同)
  • 这对我来说似乎不起作用.我得到`com.google.gson.JsonParseException:在json文件中找不到'type'成员 (6认同)
  • 我收到此错误“com.google.gson.JsonParseException:在预期的接口包装器中找不到“类型”成员” (2认同)

rap*_*ura 13

将动物放入transient,它将不会被序列化.

或者你可以通过实现defaultWriteObject(...)defaultReadObject(...)(我认为那就是他们所谓的......)自己序列化它

编辑此处查看有关"编写实例创建器"的部分.

Gson无法反序列化接口,因为它不知道将使用哪个实现类,因此您需要为Animal提供实例创建器并设置默认值或类似值.