Подтвердить что ты не робот

Дезертирование абстрактного класса в Gson

У меня есть объект tree в формате JSON. Я пытаюсь десериализоваться с Gson. Каждый node содержит свои дочерние узлы как поля типа объекта Node. node - это интерфейс, который имеет несколько конкретных реализаций класса. Во время процесса десериализации, как я могу сообщить Gson, какой конкретный класс реализовать при десериализации node, если я не знаю априори, к какому типу принадлежит node? Каждый node имеет поле члена, задающее тип. Есть ли способ получить доступ к полю, когда объект находится в сериализованной форме, и каким-то образом передать этот тип Gson?

Спасибо!

4b9b3361

Ответ 1

Я бы предложил добавить JsonDeserializer для Node s:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Node.class, new NodeDeserializer())
    .create();

Вы сможете получить доступ к JsonElement, представляющему node в методе десериализатора, преобразовать его в JsonObject и получить поле, которое указывает тип. Затем вы можете создать экземпляр правильного типа Node на основе этого.

Ответ 2

Вам нужно будет зарегистрировать JSONSerializer и JSONDeserializer. Также вы можете реализовать универсальный адаптер для всех ваших интерфейсов следующим образом:

  • Во время сериализации: добавьте META-информацию о фактическом типе типа impl.
  • Во время DeSerialization: Получить эту метаинформацию и вызвать JSONDeserailize этого класса

Вот реализация, которую я использовал для себя и прекрасно работаю.

public class PropertyBasedInterfaceMarshal implements
        JsonSerializer<Object>, JsonDeserializer<Object> {

    private static final String CLASS_META_KEY = "CLASS_META_KEY";

    @Override
    public Object deserialize(JsonElement jsonElement, Type type,
            JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObj = jsonElement.getAsJsonObject();
        String className = jsonObj.get(CLASS_META_KEY).getAsString();
        try {
            Class<?> clz = Class.forName(className);
            return jsonDeserializationContext.deserialize(jsonElement, clz);
        } catch (ClassNotFoundException e) {
            throw new JsonParseException(e);
        }
    }

    @Override
    public JsonElement serialize(Object object, Type type,
            JsonSerializationContext jsonSerializationContext) {
        JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass());
        jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY,
                object.getClass().getCanonicalName());
        return jsonEle;
    }

}

Затем вы можете зарегистрировать этот адаптер для всех своих интерфейсов следующим образом

Gson gson = new GsonBuilder()
        .registerTypeAdapter(IInterfaceOne.class,
                new PropertyBasedInterfaceMarshal())
        .registerTypeAdapter(IInterfaceTwo.class,
                new PropertyBasedInterfaceMarshal()).create();

Ответ 3

Насколько я могу судить, это не работает для типов, отличных от коллекции, или, более конкретно, ситуаций, когда конкретный тип используется для сериализации, а тип интерфейса используется для десериализации. То есть, если у вас есть простой класс, реализующий интерфейс, и вы сериализуете конкретный класс, тогда укажите интерфейс для десериализации, вы попадете в неустранимую ситуацию.

В приведенном выше примере адаптер типа зарегистрирован в интерфейсе, но при сериализации с использованием конкретного класса он не будет использоваться, то есть данные CLASS_META_KEY никогда не будут установлены.

Если вы укажете адаптер в качестве иерархического адаптера (тем самым сообщив gson о его использовании для всех типов в иерархии), вы окажетесь в бесконечном цикле, поскольку сериализатор будет просто продолжать называть себя.

Кто-нибудь знает, как сериализоваться из конкретной реализации интерфейса, а затем десериализовать, используя только интерфейс и экземпляр InstanceCreator?

По умолчанию кажется, что gson создаст конкретный экземпляр, но не установит его.

Проблема регистрируется здесь:

http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

Ответ 4

Вы должны использовать класс TypeToken из Google Gson. Конечно, вам понадобится общий класс T, чтобы он работал.

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();

gson.toJson(foo, fooType);

gson.fromJson(json, fooType);