![](/img/trans.png)
[英]Deserialize Generic Type using ReferenceTypeDeserializer with Jackson & Spring
[英]Using Jackson, how can I deserialize values using static factory methods that return wrappers with a generic type?
使用 Jackson,我想將一些值反序列化為通用包裝對象,我為每種類型都有一個特定的 static 工廠方法。
然而,Jackson 似乎並沒有接受這一間接層,即使我用@JsonCreator
注釋工廠方法。
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:無法構造
Wrapped
實例(不存在 Creator,如默認構造函數):沒有字符串參數構造函數/工廠方法可從字符串值('Carl')反序列化
我怎樣才能使 Jackson 使用返回具有通用類型的包裝器的工廠方法?
這個獨立的代碼說明了我的問題:
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中。
一種解決方案,添加一個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;
}
}
並更改請求定義以使用 Mode.DELEGATING
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 ;
}
}
@p3consulting 的回答讓我朝着正確的方向前進,但它導致了完全不同的事情。
Jackson 有一個叫做Converter的東西,它完全符合我的要求。
我為每個包裝值類型創建了轉換器,
然后在請求中注釋屬性以使用這些轉換器:
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;
}
}
我不敢相信這是如此簡單,但我花了這么長時間才找到。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.