Mark Korzhov
Mark Korzhov

Reputation: 2159

How to use Jackson's TypeReference with generics?

For json mapping I use the following method:

public static <T> T mapJsonToObject(String json, T dtoClass) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    return mapper.readValue(json, new TypeReference<RestResponse<UserDto>>() {
    });
}

And UserDto looks like this:

@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDto {

    @JsonProperty("items")
    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }
}

I want to improve this method of mapping without being attached to a UserDto class, and replacing it with a generic.

Is it possible? And How?

Thanks.

Upvotes: 15

Views: 24130

Answers (4)

Mohammed Mukhtar
Mohammed Mukhtar

Reputation: 1741

This is an example of parsing simple List based generics with Jackson, with a simple Java annotation!

package innovate.tamergroup.lastmiledelivery.loader.otm.models;

import java.util.List;

import javax.annotation.Generated;

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
    "hasMore",
    "limit",
    "count",
    "offset",
    "items",
    "links"
})
@Generated("jsonschema2pojo")
public class OTMListWrapper<T> {

@JsonProperty("hasMore")
private Boolean hasMore;
@JsonProperty("limit")
private Long limit;
@JsonProperty("count")
private Long count;
@JsonProperty("offset")
private Long offset;
@JsonTypeInfo(use=JsonTypeInfo.Id.NONE, include=JsonTypeInfo.As.PROPERTY, property="items")
private List<T> items = null;
@JsonProperty("links")
private List<OTMLink> links = null;

@JsonProperty("hasMore")
public Boolean getHasMore() {
    return hasMore;
}

@JsonProperty("hasMore")
public void setHasMore(Boolean hasMore) {
    this.hasMore = hasMore;
}

public OTMListWrapper<T> withHasMore(Boolean hasMore) {
    this.hasMore = hasMore;
    return this;
}

@JsonProperty("limit")
public Long getLimit() {
    return limit;
}

@JsonProperty("limit")
public void setLimit(Long limit) {
    this.limit = limit;
}

public OTMListWrapper<T> withLimit(Long limit) {
    this.limit = limit;
    return this;
}

@JsonProperty("count")
public Long getCount() {
    return count;
}

@JsonProperty("count")
public void setCount(Long count) {
    this.count = count;
}

public OTMListWrapper<T> withCount(Long count) {
    this.count = count;
    return this;
}

@JsonProperty("offset")
public Long getOffset() {
    return offset;
}

@JsonProperty("offset")
public void setOffset(Long offset) {
    this.offset = offset;
}

public OTMListWrapper<T> withOffset(Long offset) {
    this.offset = offset;
    return this;
}

@JsonProperty("items")
public List<T> getItems() {
    return items;
}

@JsonProperty("items")
public void setItems(List<T> items) {
    this.items = items;
}

public OTMListWrapper<T> withItems(List<T> items) {
    this.items = items;
    return this;
}

@JsonProperty("links")
public List<OTMLink> getLinks() {
    return links;
}

@JsonProperty("links")
public void setLinks(List<OTMLink> links) {
    this.links = links;
}

public OTMListWrapper<T> withLinks(List<OTMLink> links) {
    this.links = links;
    return this;
}

}

Upvotes: 0

Artanis Zeratul
Artanis Zeratul

Reputation: 1003

Try this:

    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.lang.reflect.Array;
    import java.util.Arrays;
    
    ...        


    public static <TargetType> List<TargetType> convertToList(String jsonString, Class<TargetType> targetTypeClass) {
        List<TargetType> listOfTargetObjects = null;
        ObjectMapper objectMapper = new ObjectMapper();
        TargetType[] arrayOfTargetType = (TargetType[]) Array.newInstance(targetTypeClass, 0);

        try {
            listOfTargetObjects = (List<TargetType>) Arrays.asList(objectMapper.readValue(jsonString, arrayOfTargetType.getClass()));
        } catch (JsonMappingException jsonMappingException) {
            listOfTargetObjects = null;
        } catch (JsonProcessingException jsonProcessingException) {
            listOfTargetObjects = null;
        } catch (Exception exception) {
            listOfTargetObjects = null;
        }

        return listOfTargetObjects;
    }
...

Upvotes: 0

Marcel Baumann
Marcel Baumann

Reputation: 181

One approach will be to define a Jackson JavaType representing a list of items of type clazz. You still need to have access to the class of the generic parameter at runtime. The usual approach is something like

<T> class XX { XX(Class<T> clazz, ...) ... } 

to pass the class of the generic parameter into the generic class at construction.

Upon access to the Class clazz variable you can construct a Jackson JavaType representing, for example, a list of items of class clazz with the following statement.

JavaType itemType = mapper.getTypeFactory().constructCollectionType(List.class, clazz);

I hope it helped. I am using this approach in my own code.

Upvotes: 5

StaxMan
StaxMan

Reputation: 116630

TypeReference requires you to specify parameters statically, not dynamically, so it does not work if you need to further parameterize types.

What I think you need is JavaType: you can build instances dynamically by using TypeFactory. You get an instance of TypeFactory via ObjectMapper.getTypeFactory(). You can also construct JavaType instances from simple Class as well as TypeReference.

Upvotes: 11

Related Questions