Auto-value-gson With An Interface Error, Register An Instancecreator?
Solution 1:
InstanceCreator
is not often used in Gson, suggested by Gson in such cases making some confusion, and usually can be replaced with type adapters (factories). The InstanceCreator
interface can only create a default instance that won't be merged with the JSON you're trying to deserialize. For example:
{"name":"13289/john-doe","human_variable":"John Doe"}
publicstatic Human create() {
returnnewAutoValue_Human("anonymous", null);
}
privatestaticfinalGsongson=newGsonBuilder()
.registerTypeAdapter(Species.class, (InstanceCreator<Species>) type -> Human.create())
.create();
finalSpeciesspecies= gson.fromJson(jsonReader, Species.class);
System.out.println(species.name());
Output:
anonymous
In this case you'd bind the Species
interface with the default Human
instance only. According to that, species.name()
would return anonymous
only regardless the JSON (Gson internal ReflectiveTypeAdapterFactory.Adapter
just skips all JSON fields (actually, it collects all fields first against the given field declared type, not an actual object type after the InstanceCreator
-created instance is created) because it's an interface -- not sure if it's not a bug though).
What you really need here is the following steps:
- Use
com.ryanharter.auto.value:auto-value-gson:...
if you're not using yet. - Register the
Human
type adapter factory created using theauto-value-gson
extension. - Deserialize your JSON as a concrete
Human
instance rather than aSpecies
instance.
For example:
@GsonTypeAdapterFactoryabstractclassHumanAdapterFactoryimplementsTypeAdapterFactory {
publicstatic TypeAdapterFactory create() {
returnnewAutoValueGson_HumanAdapterFactory();
}
}
privatestaticfinalGsongson=newGsonBuilder()
.registerTypeAdapterFactory(HumanAdapterFactory.create())
.create();
finalHumanhuman= gson.fromJson(jsonReader, Human.class);
System.out.println(human.name());
System.out.println(human.humanVariable());
Output:
13289/john-doe John Doe
This is the solution I would recommend the most.
If, for any justified reason, you really need to deserialize a JSON as an unknown Species
instance and resolve its type dynamically, you could create a more complex solution. One of its "classic" solutions is resolving an object type by its special JSON property (inspired by RuntimeTypeAdapterFactory coming from the Gson extras, not published as an artifact though):
{"type":"human","name":"13289/john-doe","human_variable":"John Doe"}
privatestaticfinalGsongson=newGsonBuilder()
// We'll ask Gson for it ourselves
.registerTypeAdapterFactory(HumanAdapterFactory.create())
.registerTypeAdapterFactory(newTypeAdapterFactory() {
@Overridepublic <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Check whether we can support the given typeif ( Species.class.isAssignableFrom(typeToken.getRawType()) ) {
finalTypeAdapterFactorycurrentTypeAdapterFactory=this;
// And get the "original" type adapter@SuppressWarnings("unchecked")final TypeAdapter<Species> delegateTypeAdapter = (TypeAdapter<Species>) gson.getDelegateAdapter(this, typeToken);
final TypeAdapter<Species> speciesTypeAdapter = newTypeAdapter<Species>() {
// Type tokens can be static since they are immutabeprivate/*static*/final TypeToken<Human> humanTypeToken = TypeToken.get(Human.class);
// JsonParser seems to be immutable as wellprivate/*static*/finalJsonParserjsonParser=newJsonParser();
@Overridepublicvoidwrite(final JsonWriter out, final Species value)throws IOException {
delegateTypeAdapter.write(out, value);
}
@Overridepublic Species read(final JsonReader in)throws IOException {
// Caching the current value to a JSON treefinalJsonElementjsonElement= jsonParser.parse(in);
finalStringtype= jsonElement.getAsJsonObject()
.getAsJsonPrimitive("type")
.getAsString();
final TypeAdapter<? extendsSpecies> typeAdapter;
// Now trying to resolve an appropriate type adapterswitch ( type ) {
case"human":
typeAdapter = gson.getDelegateAdapter(currentTypeAdapterFactory, humanTypeToken);
break;
default:
thrownewMalformedJsonException("Unknown type: " + type);
}
// At this point the JsonReader is moved formed due to the previous read, but we have the JSON treereturn typeAdapter.fromJsonTree(jsonElement);
}
}.nullSafe();
@SuppressWarnings("unchecked")final TypeAdapter<T> castSpeciesTypeAdapter = (TypeAdapter<T>) speciesTypeAdapter;
return castSpeciesTypeAdapter;
}
returnnull;
}
})
.create();
finalSpeciesspecies= gson.fromJson(jsonReader, Species.class);
System.out.println(species.getClass());
System.out.println(species.name());
Output:
class q43267910.AutoValue_Human 13289/john-doe
Post a Comment for "Auto-value-gson With An Interface Error, Register An Instancecreator?"