в итоге.

огда не делал много с сериализацией, но я пытаюсь использовать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);
}
}

Так что это хорошо сериализуется - но по понятным причинам отбрасывает информацию о типе на Animal:

{"mDescription":"This is a public exhibit.","mAnimal":{"mName":"Cat","mHabbit":"Playing with yarn"}}

Это вызывает реальные проблемы для десериализации, хотя:

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.

Я понимаю, почему это происходит, но у меня возникли проблемы с определением правильной схемы решения этой проблемы. Я посмотрел вруководство но это не касалось этого напрямую.

Ответы на вопрос(3)

Решение Вопроса

transient, тогда он не будет сериализован.

Или вы можете сериализовать его самостоятельно, внедривdefaultWriteObject(...) а такжеdefaultReadObject(...) (Я думаю, вот как они назывались ...)

РЕДАКТИРОВАТЬ Смотрите часть о «Написание создателя экземпляра»Вот.

Gson не может десериализовать интерфейс, поскольку он не знает, какой класс реализации будет использоваться, поэтому вам нужно предоставить создателя экземпляра для вашего Animal и установить значение по умолчанию или подобное.

 Ben Flynn25 янв. 2011 г., 18:55
Я думаю, что мне нужно больше, чем десериализатор, потому что Animal не сериализуется с какой-либо информацией типа. Предположим, есть информация о типе, кто возьмет на себя ответственность за десериализацию реализующих классов?
 Ben Flynn07 мар. 2011 г., 16:55
Да, в конце концов я сделал это: сериализовав для Выставки извлек класс, реализующий Animal, сериализовав имя класса, затем вызвав соответствующий сериализатор в этом классе для сериализации данных. Когда я десериализую, я использую рефлексию для создания экземпляра соответствующего десериализатора. Я уверен, что сделал это более сложным, чем нужно, но с другой стороны, это работает. знак равно
 Ben Flynn25 янв. 2011 г., 16:39
Я хочу, чтобы Animal был сериализован / десериализован. Не могли бы вы более подробно рассказать об этих методах записи и чтения? Благодарю.
 rapadura25 янв. 2011 г., 17:53
Извините, я думал о стандартных методах сериализатора для переопределения. Я вижу, что гсонgoogle-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/... что вы можете попробовать, вы можете переопределить десериализацию по умолчанию.
 rapadura26 янв. 2011 г., 10:06
Я не уверена. Сериализация работает, так как вы передаете его в фактический класс Cat, но десериализация завершается неудачно, поскольку у класса Exhibition есть только Animal, gson не знает, какой реализующий класс возродить. Вы могли бы реализовать JsonDeserializer для Animal, где вы могли бы предоставить свою собственную логику для создания Cat или Dog или чего-то еще. Это решение не очень красиво, но оно намекает на то, что вам следует сериализовать только чистые классы данных, а не классы, участвующие в наследовании и полиморфизме.

которое работает для всех случаев, когда статически известен только интерфейс.

Создать сериализатор / десериализатор:

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

Заставьте Gson использовать его для выбранного вами типа интерфейса:

Gson gson = new GsonBuilder().registerTypeAdapter(Animal.class, new InterfaceAdapter<Animal>())
                             .create();
 Ryan03 дек. 2013 г., 03:27
Исключение StackOverflow - context.serialize (объект) вызывает сам себя. Не знаю, как это сработало для вас.
 Robert18 окт. 2017 г., 09:39
Это не похоже на работу для меня. я получилcom.google.gson.JsonParseException: no 'type' member found in json file.
 Jeremy Rocher29 окт. 2014 г., 16:10
@ maciek-makowski Этот код прост и красив, вы знаете, откуда он взялся? Если это ваше, то какая лицензия? Спасибо
 Srneczek04 сент. 2016 г., 23:27
@ Райан прав, context.serailize (object) оказывается в бесконечном цикле - как это работает для остальных из вас, ребята?
 narthi04 янв. 2015 г., 22:00
@Hoten: это работает, потому что GSON учитывает как объявленный тип, так и фактический тип при выборе сериализатора. Когда поле имеет типAnimal GSON предпочтет адаптер типа дляAnimal интерфейс, если такой был зарегистрирован, даже если фактический типCat, вcontext.serialize(object) вызов, однако, нет «объявленного типа», чтобы рассмотреть, это простоCat Это означает, что адаптер типа не будет использоваться.

объявленный типом переменной-члена является интерфейс / абстрактный класс. Это не будет работать, если объявленный тип является подклассом / субинтерфейсом / субабстрактным классом, если мы не зарегистрируем их всеregisterTypeAdapter(), Мы можем избежать регистрации по одному с использованиемregisterTypeHierarchyAdapter, но я понимаю, что это вызоветStackOverflowError из-за бесконечного цикла. (Пожалуйста, прочитайте справочный раздел ниже)

Короче говоря, мое обходное решение выглядит немного бессмысленным, но оно работает безStackOverflowError.

@Override
public JsonElement serialize(T object, Type interfaceType, JsonSerializationContext context) {
    final JsonObject wrapper = new JsonObject();
    wrapper.addProperty("type", object.getClass().getName());
    wrapper.add("data", new Gson().toJsonTree(object));
    return wrapper;
}

Я использовал другой новыйGson экземпляр работы в качестве сериализатора / десериализатора по умолчанию, чтобы избежать бесконечного цикла. Недостатком этого решения является то, что вы также потеряете другиеTypeAdapter также, если у вас есть пользовательская сериализация для другого типа, и она появляется в объекте, она просто потерпит неудачу.

Тем не менее, я надеюсь на лучшее решение.

Ссылка

Согласно Gson 2.3.1 документация дляJsonSerializationContext а такжеJsonDeserializationContext

Вызывает сериализацию по умолчанию для указанного объекта, передавая информацию определенного типа. Он никогда не должен вызываться для элемента, полученного в качестве параметра метода JsonSerializer.serialize (Object, Type, JsonSerializationContext). Это приведет к бесконечному циклу, поскольку Gson, в свою очередь, снова вызовет пользовательский сериализатор.

а также

Вызывает десериализацию по умолчанию для указанного объекта. Он никогда не должен вызываться для элемента, полученного в качестве параметра метода JsonDeserializer.deserialize (JsonElement, Type, JsonDeserializationContext). Это приведет к бесконечному циклу, поскольку Gson, в свою очередь, снова вызовет пользовательский десериализатор.

Это делает вывод, что приведенная ниже реализация вызовет бесконечный цикл и вызоветStackOverflowError в итоге.

@Override
public JsonElement serialize(Animal src, Type typeOfSrc,
        JsonSerializationContext context) {
    return context.serialize(src);
}

Ваш ответ на вопрос