java 使用Jackson,我如何使用返回泛型类型 Package 器的静态工厂方法反序列化值?

y1aodyip  于 2023-01-11  发布在  Java
关注(0)|答案(2)|浏览(106)

使用Jackson,我想将一些值反序列化为泛型 Package 器对象,对于这些对象,我为每种类型都有一个特定的静态工厂方法。
然而,Jackson似乎并没有注意到这一层的间接性,即使我使用@JsonCreator注解工厂方法。
定义无效异常:无法构造Wrapped的示例(不存在创建者,如默认构造函数):没有字符串参数构造函数/工厂方法可从字符串值(“Carl”)反序列化

如何让Jackson使用返回泛型类型 Package 器的工厂方法?

这个自包含的代码说明了我的问题:

class Request {
    // I want to deserialize into these fields
    @JsonProperty Wrapped<Person> person;
    @JsonProperty Wrapped<Score> score;
}

class Wrapped<T> {
    // This is my generic wrapper type.
    // Its construction is non-trivial: it is impossible to first construct the value before wrapping it.
    // Therefor, construction is performed by the factory methods of the concrete value classes (Person, Score, ...).

    // Let's say for simplicity that it did have a simple constructor:
    T value;
    public Wrapped(T value) {
        this.value = value;
    }
}

class Person {
    @JsonCreator
    public static Wrapped<Person> createWrapped(String name) {
        // complex construction of the wrapped person
        return new Wrapped<>(new Person(name));
    }

    @JsonValue
    String name;
    public Person(String name) {
        this.name = name;
    }
}

class Score {
    @JsonCreator
    public static Wrapped<Score> createWrapped(int score) {
        // complex construction of the wrapped score
        return new Wrapped<>(new Score(score));
    }

    @JsonValue
    int score;
    public Score(int score) {
        this.score = score;
    }
}

class Example {
    private static final String JSON_REQUEST =
            """
            {
              "person":"Carl",
              "score":20
            }
            """;

    public static void main(String[] args) throws Exception {
        Request request = new ObjectMapper()
                .readValue(JSON_REQUEST, Request.class);
        System.out.println(request.person.value.name);
        System.out.println(request.score.value.score);
    }
}

值得注意的是,类型信息只存在于Java类中,而不应该存在于JSON中。

6kkfgxo0

6kkfgxo01#

一种解决方案是添加DTO:

public class RequestDTO {
    @JsonValue
    String  person;
    @JsonValue
    Integer score;

    /**
     * @return the person
     */
    public String getPerson() {
        return person;
    }

    /**
     * @return the score
     */
    public Integer getScore() {
        return score;
    }

    /**
     * @param person the person to set
     */
    public void setPerson(String person) {
        this.person = person;
    }

    /**
     * @param score the score to set
     */
    public void setScore(Integer score) {
        this.score = score;
    }

    public RequestDTO() {
        
    }
    
    public RequestDTO(String person, Integer score) {
        this.person = person;
        this.score = score;
    }
}

并将请求定义更改为使用模式。

public class Request {
    // I want to deserialize into these fields
    @JsonProperty Wrapped<Person> person;
    @JsonProperty Wrapped<Score> score;

    @JsonCreator(mode=Mode.DELEGATING)
    public static Request createWrapped(RequestDTO requestDTO) {
        // complex construction of the wrapped person
        Request req = new Request();
        req.person = new Wrapped<>(new Person(requestDTO.getPerson()));
        req.score = new Wrapped<>(new Score(requestDTO.getScore()));
        
        return req ;
    }
}
20jt8wwn

20jt8wwn2#

@p3consulting的回答让我找到了正确的方向,但却导致了完全不同的结果。
Jackson有一个叫Converter的东西,它完全符合我的要求。
我为每个 Package 的值类型创建了转换器,
然后注解请求中的属性以使用这些转换器:

class Request {
    @JsonDeserialize(converter = WrappedPersonDeserializer.class)
    Wrapped<Person> person;

    @JsonDeserialize(converter = WrappedScoreDeserializer.class)
    Wrapped<Score> score;
}
class PersonConverter
        extends StdConverter<String, Wrapped<Person>> {

    @Override
    public Wrapped<Person> convert(String value) {
        return Person.createWrapped(value);
    }
}

class ScoreConverter
        extends StdConverter<Integer, Wrapped<Score>> {

    @Override
    public Wrapped<Score> convert(Integer score) {
        return Score.createWrapped(score);
    }
}

对于具有更复杂签名的工厂方法,可以通过使用DTO来实现,例如:

class WrappedPersonConverter2
        extends StdConverter<WrappedPersonConverter2.DTO, Wrapped<Person>> {

    @Override
    public Wrapped<Person> convert(WrappedPersonConverter2.DTO dto) {
        return Person.createWrapped(dto.first, dto.last);
    }

    public static class DTO {
        public int first;
        public int last;
    }
}

我不敢相信这是如此简单,但我花了这么长时间才找到。

相关问题