InstanceCreator
не часто используется в Gson, предложенный Gson в таких случаях, внося некоторую путаницу, и обычно может быть заменен адаптерами типов (фабриками). Интерфейс InstanceCreator
может создать только экземпляр по умолчанию, который не будет объединен с JSON, который вы пытаетесь десериализовать. Например:
{
"name": "13289/john-doe",
"human_variable": "John Doe"
}
public static Human create() {
return new AutoValue_Human("anonymous", null);
}
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Species.class, (InstanceCreator<Species>) type -> Human.create())
.create();
final Species species = gson.fromJson(jsonReader, Species.class);
System.out.println(species.name());
Выход:
анонимный
В этом случае вы бы связали интерфейс Species
только с экземпляром по умолчанию Human
. В соответствии с этим species.name()
вернет anonymous
только независимо от JSON (внутренний ReflectiveTypeAdapterFactory.Adapter
Gson просто пропускает все поля JSON (фактически, он сначала собирает все поля для заданного объявленного типа поля, а не фактического типа объекта после создания InstanceCreator
-created экземпляра) потому что это интерфейс - хотя не уверен, что это не ошибка).
Что вам действительно нужно, так это следующие шаги:
- Используйте
com.ryanharter.auto.value:auto-value-gson:...
, если вы еще не используете.
- Зарегистрируйте фабрику адаптеров типа
Human
, созданную с использованием расширения auto-value-gson
.
- Десериализуйте свой JSON как конкретный
Human
экземпляр, а не Species
.
Например:
@GsonTypeAdapterFactory
abstract class HumanAdapterFactory
implements TypeAdapterFactory {
public static TypeAdapterFactory create() {
return new AutoValueGson_HumanAdapterFactory();
}
}
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(HumanAdapterFactory.create())
.create();
final Human human = gson.fromJson(jsonReader, Human.class);
System.out.println(human.name());
System.out.println(human.humanVariable());
Выход:
13289 / john-doe
Джон Доу
Это решение, которое я бы рекомендовал больше всего.
Если по какой-либо обоснованной причине вам действительно нужно десериализовать JSON как неизвестный Species
экземпляр и динамически разрешить его тип, вы можете создать более сложное решение. Одно из его «классических» решений - определение типа объекта с помощью его специального свойства JSON (навеянного RuntimeTypeAdapterFactory из дополнительных компонентов Gson, но не опубликованных как артефакт):
{
"type": "human",
"name": "13289/john-doe",
"human_variable": "John Doe"
}
private static final Gson gson = new GsonBuilder()
// We'll ask Gson for it ourselves
.registerTypeAdapterFactory(HumanAdapterFactory.create())
.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Check whether we can support the given type
if ( Species.class.isAssignableFrom(typeToken.getRawType()) ) {
final TypeAdapterFactory currentTypeAdapterFactory = this;
// And get the "original" type adapter
@SuppressWarnings("unchecked")
final TypeAdapter<Species> delegateTypeAdapter = (TypeAdapter<Species>) gson.getDelegateAdapter(this, typeToken);
final TypeAdapter<Species> speciesTypeAdapter = new TypeAdapter<Species>() {
// Type tokens can be static since they are immutabe
private /*static*/ final TypeToken<Human> humanTypeToken = TypeToken.get(Human.class);
// JsonParser seems to be immutable as well
private /*static*/ final JsonParser jsonParser = new JsonParser();
@Override
public void write(final JsonWriter out, final Species value)
throws IOException {
delegateTypeAdapter.write(out, value);
}
@Override
public Species read(final JsonReader in)
throws IOException {
// Caching the current value to a JSON tree
final JsonElement jsonElement = jsonParser.parse(in);
final String type = jsonElement.getAsJsonObject()
.getAsJsonPrimitive("type")
.getAsString();
final TypeAdapter<? extends Species> typeAdapter;
// Now trying to resolve an appropriate type adapter
switch ( type ) {
case "human":
typeAdapter = gson.getDelegateAdapter(currentTypeAdapterFactory, humanTypeToken);
break;
default:
throw new MalformedJsonException("Unknown type: " + type);
}
// At this point the JsonReader is moved formed due to the previous read, but we have the JSON tree
return typeAdapter.fromJsonTree(jsonElement);
}
}.nullSafe();
@SuppressWarnings("unchecked")
final TypeAdapter<T> castSpeciesTypeAdapter = (TypeAdapter<T>) speciesTypeAdapter;
return castSpeciesTypeAdapter;
}
return null;
}
})
.create();
final Species species = gson.fromJson(jsonReader, Species.class);
System.out.println(species.getClass());
System.out.println(species.name());
Выход:
класс q43267910.AutoValue_Human
13289 / john-doe
person
Lyubomyr Shaydariv
schedule
07.04.2017