RuntimeTypeAdapterFactory says “type” not defined
Asked Answered
A

3

5

I am dealing with a situation where I have polymorphic classes that I need to deserialize.

Class Pen{
  String name;
  List<Animal> animals;
}

//Animal can be an interface or parent class: I am flexible

Class Animal{
  AnimalType type;//enum
  int legs;
}

enum AnimalType{
  dog,cat,pig,chicken;
}

Class AnimalDog extends Animal{
  //…
}

Class AnimalCat extends Animal{
  //…
}


Class AnimalPig extends Animal{
  //…
}

then I create my Gson instance with

public static Gson instanceUpperCamelCaseWithTypeAdapterFactory() {
    if (null == sGsonUpperCamelCase) {
        final RuntimeTypeAdapterFactory<Animal> typeFactory = RuntimeTypeAdapterFactory
                .of(Animal.class, “type")
                .registerSubtype(AnimalDog.class, “dog”)
                .registerSubtype(AnimalCat.class, “cat”)
                .registerSubtype(AnimalPig.class, “pig”);

        sGsonUpperCamelCase = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                .registerTypeAdapterFactory(typeFactory).create();
    }//the naming policy is because server sends me upper case fields whereas Java fields are lowercase.
    return sGsonUpperCamelCase;
}

To get the animals from a json that contains a list of animals, I do

List<Animal> animals = gson.fromJson(json, new TypeToken<List<Animal>>() {}.getType());

I am completely a newbie to Gson. Completely. So without confusing me too much, how might I solve this problem?

Error trace:

com.google.gson.JsonParseException: cannot deserialize class com.company.appname.data.model.Animal because it does not define a field named type
com.company.appname.utils.RuntimeTypeAdapterFactory$1.read(RuntimeTypeAdapterFactory.java:204)
com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217)
com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
com.google.gson.Gson.fromJson(Gson.java:861)
com.google.gson.Gson.fromJson(Gson.java:826)
com.google.gson.Gson.fromJson(Gson.java:775)

I run the json through an online validator, there is no problem with it. It has a number of items. Here I show two.

{“Animals”:[{  
           “id":9,
           “type”:”dog”,
           “name”:”maximus”
        },
        {  
           “id":10,
           “type”:”cat”,
           “name”:”meowy”,
           “yarns”:5,
           “nice”:true
        }]}
Agrarian answered 2/2, 2016 at 19:16 Comment(10)
It seems there is a silly person tracking me to give me -1 for every question. Thanks to whoever up-voted to counter that silly person. And thanks for any help I get solving this problem.Agrarian
try chaining type to String and provide the label to the super classMisquotation
@Misquotation I don't understand the comment. "chaining type to String"?Agrarian
change AnimalType type; to String type;, and assign "dog" to it from AnimalDog (do the same for the other animals)Misquotation
Ah I find the bug, but not the solution (+1). Once Gson uses the "type" field to find the appropriate subclass, it sets the actual type field to null. But in my case I use the field to do more work. Is there a way to keep Gson from swallowing up the value of the type field?Agrarian
did you try to initialize it from the children ?Misquotation
??? You have all my code. I get json from server. to get type I do animal.getType() and the value is null. And that's after changing type to String instead of using enum.Agrarian
could you post the json?Misquotation
type is a String. How should it be parsed in a Enum? Could you try changing the enum like: enum AnimalType{ @SerializedName("dog") dog, @SerializedName("cat") cat, @SerializedName("pig") pig, @SerializedName("chicken") chicken; }Misquotation
I stopped using enum altogether and use String. The type value still gets swallowed. In fact, if I remove the type field from the POJO it still works as long as the json from the server has the type field. So the field is thought to be for the RuntimeTypeAdapterFactory and not for the object and so it is not serialized as part of the POJO.Agrarian
Y
5

RuntimeTypeAdapterFactory.class line number -> 203

need replace

JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);

with

JsonElement labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
Yeaton answered 20/3, 2018 at 11:30 Comment(0)
G
0

If anyone stumbles on this old question :-)

You can set maintainType = true in

RuntimeTypeAdapterFactory.of(Animal.class, “type", true)

Hope this helpes someone :-)

Gladygladys answered 17/4 at 10:51 Comment(0)
P
-1

i've found bug in RuntimeTypeAdapterFactory (line 185, method create(), and resolved this issue, need replace

if (type.getRawType() != baseType)

with

if (null == type || !baseType.isAssignableFrom(type.getRawType()))

Then RuntimeTypeAdapterFactory works well as expected.

See link registered issue with suggested fix

Pushcart answered 12/2, 2016 at 18:16 Comment(2)
This isn't a proper solution, nor does it fix the issue.Kraus
Could you add more details, what exactly wrong? This solution is checked for some years.Pushcart

© 2022 - 2024 — McMap. All rights reserved.