Search code examples
javaandroidjacksonguiceroboguice

Deserializing a Generic Type with Jackson


I am trying to make a class that uses the Jackson to deserialize POJO's.

It looks like this...

public class DeserialiserImp<T> implements Deserialiser<T> {

        protected ObjectMapper objectMapper = new ObjectMapper();

        @Override
        public T get(String content, Class clazz) throws IOException {
            return (T) objectMapper.readValue(content, clazz);
        }

        @Override
        public List<T> getList(String content, Class clazz) throws IOException {
            return objectMapper.readValue(content, TypeFactory.collectionType(ArrayList.class, clazz));
        }

    }

I have 2 questions about this implementation.

The first is that I am passing the class type into the methods so the objectmapper knows the type that should deserialize. Is there a better way using generics?

Also in the get method I am casting an object returned from the objectMapper to T. This seems particularly nasty way of doing it as I have to cast T here and then I have to also cast the object type from the method which is calling it.

I am using Roboguice in this project so it would be nice if I could change the type through injection and then annotate the object which the Generic type I need it to return. I read about TypeLiteral and wondering if it could solve this problem?


Solution

  • So I think I figured it out in the end. Please comment if you see something wrong with what I am doing.

    The interface is defined like so...

    public interface Deserialiser<T> {
    
        T get(String content) throws IOException;
    
        List<T> getList(String content) throws IOException;
    }
    

    The implementation of the interface is like this...

    public class DeserialiserImp<T> implements Deserialiser<T> {
    
        private ObjectMapper objectMapper = new ObjectMapper();
        private final Class<T> klass;
    
        @Inject
        public DeserialiserImp(TypeLiteral<T> type){
            this.klass = (Class<T>) type.getRawType();
        }
    
        @Override
        public T get(String content) throws IOException {
            return objectMapper.readValue(content, klass);
        }
    
        @Override
        public List<T> getList(String content) throws IOException {
            return objectMapper.readValue(content, TypeFactory.collectionType(ArrayList.class, klass));
        }
    
    }
    

    I bind the 2 like so..

        bind(new TypeLiteral<Deserialiser<User>>(){}).annotatedWith(Names.named("user")).to(new TypeLiteral<DeserialiserImp<User>>(){});
    

    Then all I need to do to use it is this...

    @Inject
    @Named("user")
    private Deserialiser<User> deserialiserImp;
    
    public void test(String userString) {
        User user = deserialiserImp.get(UserString);
    }
    

    This pattern could also work well if the class as an abstract class to use in a DAO object

    This article helped me