Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 336
jiduvah Avatar asked Oct 29 '12 09:10

jiduvah


People also ask

What is Jackson's TypeReference?

Class TypeReference<T> This generic abstract class is used for obtaining full generics type information by sub-classing; it must be converted to ResolvedType implementation (implemented by JavaType from "databind" bundle) to be used.

What is Jackson deserialization?

Jackson is a powerful and efficient Java library that handles the serialization and deserialization of Java objects and their JSON representations. It's one of the most widely used libraries for this task, and runs under the hood of many other frameworks.

What is the use of Jackson ObjectMapper?

The Jackson ObjectMapper can parse JSON from a string, stream or file, and create a Java object or object graph representing the parsed JSON. Parsing JSON into Java objects is also referred to as to deserialize Java objects from JSON. The Jackson ObjectMapper can also create JSON from Java objects.

What is the use of Jackson in Java?

Jackson allows you to read JSON into a tree model: Java objects that represent JSON objects, arrays and values. These objects are called things like JsonNode or JsonArray and are provided by Jackson.


2 Answers

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 ?

like image 58
Pierre-Henri Avatar answered Oct 18 '22 03:10

Pierre-Henri


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

like image 22
jiduvah Avatar answered Oct 18 '22 03:10

jiduvah