[英]Jackson deserialize JSON into generic type using property name as value to fetch type from registry
我一直在嘗試通過使用屬性值作為對要為T
反序列化的類型的引用並從注冊表中獲取類型來將 JSON 反序列化為泛型類型。
我能夠實現概念證明,如果我使用 TypeFactory.constructParametricType 提供正確的TypeFactory.constructParametricType
,我可以反序列化泛型類型,但后來我嘗試實現JsonDeserializer
但它最終再次調用自身,因為該反序列化器已注冊到Property.class
所以我嘗試的另一件事是使用新的ObjectManager
,但我真的不想創建新的 ObjectMapper 並且想使用同一個。
所以也許有人能夠指導我正確的方向。 (我知道注釋,但我不能真正使用它們,因為類型注冊表需要特定的邏輯)
這是我當前有效的臨時代碼:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Scratch {
interface Type {
@JsonIgnore
public default String getName() {
return this.getClass().getSimpleName();
}
}
static class Property<T extends Type> {
private String typeName;
private T type;
private String name;
public Property() {
}
public Property(T type, String name) {
this.typeName = type.getName();
this.type = type;
this.name = name;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
this.typeName = type.getName();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
static class TypeRegistry {
private final Map<String, Class<? extends Type>> types = new HashMap<>();
public void add(Type type) {
types.put(type.getName(), type.getClass());
}
public boolean has(String typeName) {
return types.containsKey(typeName);
}
public Class<? extends Type> get(String typeName) {
return this.types.get(typeName);
}
}
static class TextType implements Type {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
static class NumberType implements Type {
private String format;
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
public static void main(String[] args) throws Exception {
var typeRegistry = new TypeRegistry();
typeRegistry.add(new TextType());
typeRegistry.add(new NumberType());
var parameters = new ArrayList<Property<?>>();
parameters.add(new Property<>(new TextType(), "text_field"));
parameters.add(new Property<>(new NumberType(), "number_field"));
var module = new SimpleModule();
module.addDeserializer(Property.class, new JsonDeserializer<Property<?>>() {
private final ObjectMapper newObjectMapper = new ObjectMapper();
@Override
public Property<?> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
if (node == null || node.isNull()) {
return null;
}
var typeName = node.get("typeName").asText();
var type = typeRegistry.get(typeName);
return newObjectMapper.readValue(
node.toString(),
ctxt.getTypeFactory().constructParametricType(Property.class, type)
);
}
});
var objectMapper = new ObjectMapper();
var desObjectMapper = new ObjectMapper();
desObjectMapper.registerModule(module);
var jsonSingle = objectMapper.writeValueAsString(parameters.get(0));
var json = objectMapper.writeValueAsString(parameters);
var parsedSingle = objectMapper.readValue(
jsonSingle,
TypeFactory.defaultInstance().constructParametricType(
Property.class,
typeRegistry.get("TextType")
)
);
var parsedProperties = desObjectMapper.readValue(
json,
new TypeReference<List<Property<?>>>() {}
);
}
}
謝謝你的幫助!
編輯 1(添加了@michael-gantman 提出的解決方案):
var parsedProperties = objectMapper.readValue(
json,
new TypeReference<List<LinkedHashMap<String, Object>>>() {
}
);
var castedProperties = new ArrayList<Property<?>>();
for (var parsedProperty : parsedProperties) {
var typeName = (String) parsedProperty.get("typeName");
castedProperties.add(
objectMapper.convertValue(
parsedProperty,
TypeFactory.defaultInstance().constructParametricType(
Property.class,
typeRegistry.get(typeName)
)
)
);
}
這只是另一種方法。 始終將您的 Json 反序列化為Map<String, Object>
(用於 JSON 對象)或List<Object>
(用於 Json 列表)。 而不是根據您的屬性將您的地圖轉換為您的自定義類。 為此,您可以讓所有相關類都有一個接受 Map 或靜態方法的構造函數
T getInstance(Map<String, Object>)
創建一個實例。 實現 Map -> 特定類轉換比 JSON -> 特定類更簡單。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.