Stefan Lindner
Stefan Lindner

Reputation: 329

How to deserialize different subtypes with generics with one gson deserializer

I have to save configuration objects for different testcases. I decided to use GSON for that, since i have worked with that already. The problem is the structure/inheritance of the configuration file contains generics in its definition. If this is a duplicate, i have read the thread, but couldn't apply it to my problem. The code is broken down to the minimum.

The configuration class

Besides other easy to serialize components it has an ArrayList<T>.

//There are classes that inherit from that too
public class Configuration<T extends ParamSet> implements Iterable<T>{

    // this list is what i want to set from my ApplicationClass
    private ArrayList<T> _paramSets = new ArrayList<T>();

    //getter, setter and constructor
}

The ParameterSet definitions

There is nothing special in these classes. Just Integer and Boolean values.

public class ParamSet {}

public class ChildParamSet extends ParamSet{}

Abstract Application class

This is where the saving process starts. I have defined a abstract method later implemented in classes where T is known at runtime.

public abstract class ApplicationClass<T extends ParamSet>{

    private Configuration<T> config;

    // in this method i want to set either a new config or new params in the config
    public setupConfig(){
        //some checks and if the user wants to load a config from file
        loadConfig(file);       
    }
    //abstract method to be called in more concrete classes
    abstract protected Configuration<T> loadConfig(File file);
}

Concrete Application class

Well here i know the type of T and i want to call a getConfigurationmethod.

public abstract class MoreConcreteApplicationClass extends ApplicationClass<ChildParamSet>{

    //constructor and other stuff

    @Override
    protected Configuration<ChildParamSet> loadConfig(File file){
        return ConfigurationPersistenceHelper.getConfiguration(file);
    }   
}

Configuration Persistence Helper -- the problem

public class ConfigurationPersistenceHelper(){

    //i get the configuration with a linked treeMap --> wrong type 
    public static <T extends ParamSet> Configuration<T> getConfiguration(final File location){
        GsonBuilder builder = new GsonBuilder();
        GsonBuilder builder.enableComplexMapKeySerialization.create();

        final Type type = new TypeToken<Configuration<T>>(){}.getType();

        final String jsonRepresentation =  new String(Files.readAllBytes(location.toPath()));
        return gson.fromJson(jsonRepresentation, type);

    }
}

So the problem is the getConfiguration method in the ConfigurationPersistenceHelper. When i run this method, instead of an ArrayList<ChildParamSet> i get a Linked TreeMap in the configuration.

I guess this is because of type erasure for generics during runtime and my definition of the method with T extends ParamSet.

But how can i fix this? Is there another way to call the method so the type isn´t lost?

Workarounds i know of

I know i can implement the deserializer logic in the loadConfig method of every concrete application class. But this would have a lot of duplicate code as a result. This is my current solution.

Also i know i can pass the type to the serializer from the loadConfig (where the type is still available at runtime) to the getConfiguration method. Is that bad practice?

I also know i can write my own deserializer but there i would also need the type at runtime, which is currently my problem.

Upvotes: 2

Views: 386

Answers (1)

David Kraus
David Kraus

Reputation: 56

Yep, Type Erasure is the problem. Just pass in the type info like so:

abstract protected Configuration<T> loadConfig(File file, Class<T> clazz);

inside that method use a custom ParameterizedType for your ArrayList, see:

Upvotes: 1

Related Questions