Feign+Jackson:无法构造实例

Feign+Jackson: Can not construct instance

@Data
public class UPMSetResult<T> implements Serializable {
    Set<T> data;

    @Data
    public class UPMMenu implements Serializable {
        Long id;
        String title;
        String url;
        Integer type;
        String code;
        Integer sort;
        Set<UPMSetResult.UPMMenu> menus;
    }
}


@Headers({"Content-Type: application/json", "Date: {date}", "Authorization: {authorization}"})
@RequestLine("GET ?appkey={appKey}&userId={userId}")
UPMSetResult<UPMSetResult.UPMMenu> menus(@Param("appKey") String appKey, @Param("userId") Long userId, @Param("date") String date, @Param("authorization") String authorization);

Feign.builder()
            .encoder(new JacksonEncoder(objectMapper))
            .decoder(new JacksonDecoder(objectMapper))
            .target(UpmApi.class, upmProperties.getDomain() + upmProperties.getMenusUri());

{"data":[{"menus":[],"id":113308,"type":1,"code":"8f96f1f52c66338335f4041214b61fb8","url":"/allocates","sort":9999,"title":"xx","createTime":1486720879}]}

class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package model;

import java.io.Serializable;
import java.util.Set;

public class UPMSetResult<T> implements Serializable {
    Set<T> data;

    public UPMSetResult() {
    }

    public Set<T> getData() {
        return this.data;
    }

    public void setData(Set<T> data) {
        this.data = data;
    }

    public boolean equals(Object o) {
        if(o == this) {
            return true;
        } else if(!(o instanceof UPMSetResult)) {
            return false;
        } else {
            UPMSetResult other = (UPMSetResult)o;
            if(!other.canEqual(this)) {
                return false;
            } else {
                Set this$data = this.getData();
                Set other$data = other.getData();
                if(this$data == null) {
                    if(other$data != null) {
                        return false;
                    }
                } else if(!this$data.equals(other$data)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof UPMSetResult;
    }

    public int hashCode() {
        boolean PRIME = true;
        byte result = 1;
        Set $data = this.getData();
        int result1 = result * 59 + ($data == null?43:$data.hashCode());
        return result1;
    }

    public String toString() {
        return "UPMSetResult(data=" + this.getData() + ")";
    }

    public class UPMMenu implements Serializable {
        Long id;
        String title;
        String url;
        Integer type;
        String code;
        Integer sort;
        Set<UPMSetResult.UPMMenu> menus;

        public UPMMenu() {
        }

        public Long getId() {
            return this.id;
        }

        public String getTitle() {
            return this.title;
        }

        public String getUrl() {
            return this.url;
        }

        public Integer getType() {
            return this.type;
        }

        public String getCode() {
            return this.code;
        }

        public Integer getSort() {
            return this.sort;
        }

        public Set<UPMSetResult.UPMMenu> getMenus() {
            return this.menus;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public void setSort(Integer sort) {
            this.sort = sort;
        }

        public void setMenus(Set<UPMSetResult.UPMMenu> menus) {
            this.menus = menus;
        }

        public boolean equals(Object o) {
            if(o == this) {
                return true;
            } else if(!(o instanceof UPMSetResult.UPMMenu)) {
                return false;
            } else {
                UPMSetResult.UPMMenu other = (UPMSetResult.UPMMenu)o;
                if(!other.canEqual(this)) {
                    return false;
                } else {
                    label95: {
                        Long this$id = this.getId();
                        Long other$id = other.getId();
                        if(this$id == null) {
                            if(other$id == null) {
                                break label95;
                            }
                        } else if(this$id.equals(other$id)) {
                            break label95;
                        }

                        return false;
                    }

                    String this$title = this.getTitle();
                    String other$title = other.getTitle();
                    if(this$title == null) {
                        if(other$title != null) {
                            return false;
                        }
                    } else if(!this$title.equals(other$title)) {
                        return false;
                    }

                    String this$url = this.getUrl();
                    String other$url = other.getUrl();
                    if(this$url == null) {
                        if(other$url != null) {
                            return false;
                        }
                    } else if(!this$url.equals(other$url)) {
                        return false;
                    }

                    label74: {
                        Integer this$type = this.getType();
                        Integer other$type = other.getType();
                        if(this$type == null) {
                            if(other$type == null) {
                                break label74;
                            }
                        } else if(this$type.equals(other$type)) {
                            break label74;
                        }

                        return false;
                    }

                    label67: {
                        String this$code = this.getCode();
                        String other$code = other.getCode();
                        if(this$code == null) {
                            if(other$code == null) {
                                break label67;
                            }
                        } else if(this$code.equals(other$code)) {
                            break label67;
                        }

                        return false;
                    }

                    Integer this$sort = this.getSort();
                    Integer other$sort = other.getSort();
                    if(this$sort == null) {
                        if(other$sort != null) {
                            return false;
                        }
                    } else if(!this$sort.equals(other$sort)) {
                        return false;
                    }

                    Set this$menus = this.getMenus();
                    Set other$menus = other.getMenus();
                    if(this$menus == null) {
                        if(other$menus != null) {
                            return false;
                        }
                    } else if(!this$menus.equals(other$menus)) {
                        return false;
                    }

                    return true;
                }
            }
        }

        protected boolean canEqual(Object other) {
            return other instanceof UPMSetResult.UPMMenu;
        }

        public int hashCode() {
            boolean PRIME = true;
            byte result = 1;
            Long $id = this.getId();
            int result1 = result * 59 + ($id == null?43:$id.hashCode());
            String $title = this.getTitle();
            result1 = result1 * 59 + ($title == null?43:$title.hashCode());
            String $url = this.getUrl();
            result1 = result1 * 59 + ($url == null?43:$url.hashCode());
            Integer $type = this.getType();
            result1 = result1 * 59 + ($type == null?43:$type.hashCode());
            String $code = this.getCode();
            result1 = result1 * 59 + ($code == null?43:$code.hashCode());
            Integer $sort = this.getSort();
            result1 = result1 * 59 + ($sort == null?43:$sort.hashCode());
            Set $menus = this.getMenus();
            result1 = result1 * 59 + ($menus == null?43:$menus.hashCode());
            return result1;
        }

        public String toString() {
            return "UPMSetResult.UPMMenu(id=" + this.getId() + ", title=" + this.getTitle() + ", url=" + this.getUrl() + ", type=" + this.getType() + ", code=" + this.getCode() + ", sort=" + this.getSort() + ", menus=" + this.getMenus() + ")";
        }
    }
}

原因:com.fasterxml.jackson.databind.JsonMappingException:无法构造 model.UPMSetResult$UPMMenu 的实例:未找到合适的构造函数,无法从对象值反序列化(缺少默认构造函数或创建者,或者可能需要 add/enable类型信息?) 在 [来源:java.io.BufferedReader@39688e58;行:1,列:11](通过引用链:model.UPMSetResult["data"]->java.util.HashSet[0])

我解决了:只把UP Menu写成single class 而不是inner class,但是为什么呢?有人可以告诉我杰克逊为此做些什么。