7

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?

jiduvah
  • 5,108
  • 6
  • 39
  • 55
  • See http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak Jul 01 '13 at 12:46

2 Answers2

5

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?

Unfortunately not, and this is because of type erasure.

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.

Do this instead :

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

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?

I don't really understand what you want to achieve, but since you need to pass the class anyways, is that still possible ?

Pierre-Henri
  • 1,495
  • 10
  • 14
  • My point about using TypeLiteral comes from this question http://stackoverflow.com/questions/3370641/how-does-guices-typeliteral-work It says that "The trick that is used here is that the signatures of generic super types are stored in subclasses and thus survive erasure." I am hoping not to pass in the type. – jiduvah Oct 29 '12 at 09:58
  • 1
    Ok I understand. I don't see any practical solution here, you need to store the type somewhere (plain Class object as field, using TypeLiteral etc ...) to retrieve it at runtime. I don't think passing the type each time is a major problem, since it is required by all unmarshalling libraries. I would be interested to see an other approach though. – Pierre-Henri Oct 29 '12 at 10:32
  • Well, I am trying to make the code cleaner. At the moment I pass in the type as a generic when instantiating the deserialiserImp, I have to pass in the class into the method, then I also need to cast the object that I receive. So thats 3 times I reference the type. It seems very messey – jiduvah Oct 29 '12 at 12:01
  • I was referring to something else but I realised I misunderstood something. Thanks – jiduvah Oct 29 '12 at 12:14
  • I like it. I did not know that TypeLiteral type would be injected automatically. Nice and clean. – Pierre-Henri Oct 29 '12 at 15:09
5

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

jiduvah
  • 5,108
  • 6
  • 39
  • 55