我有一个像这样的JSON文件:
{
"Properties": {
"String": "one-string-value",
"Number": 123,
"LiteralList": [
"first-value",
"second-value"
],
"Boolean": true,
"ReferenceForOneValue": {
"Ref": "MyLogicalResourceName"
}
}
}
我想使用Jackson将其反序列化为正确的Map<String,Property>
。为此,我像这样使用TypeReference
:
public class Template {
@JsonProperty("Properties")
@JsonDeserialize(using = PropertyDeserializer.class)
public Map<String, Object> propertyList;
}
public class PropertyDeserializer extends JsonDeserializer<Map<String, Property>> {
public Map<String, Property> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Map<String,Property> result =
mapper.readValue(jsonParser, new TypeReference<Map<String,Property>>() {});
return result;
}
}
每个Property
只能使用一些值:字符串,整数,布尔值,字符串列表或具有一个键“ Ref”的另一个对象。所以我写了这个:
public class Property {
private Object value;
public Property(String value) {
this.value = value;
}
public Property(int value) {
this.value = value;
}
public Property(List<String> values) {
this.value = values;
}
public Property(boolean value) {
this.value = value;
}
public Property(Ref reference) {
this.value = reference;
}
}
public class Ref {
private String reference;
@JsonCreator
public Ref(@JsonProperty("Ref") String reference) {
this.reference = reference;
}
}
但是,这不起作用:
对于LiteralList
属性,我收到错误无法从START_ARRAY令牌中反序列化Property
实例
对于ReferenceForOneValue
属性,我收到错误消息无法构造Property
的实例(尽管至少存在一个Creator):无法从Object值反序列化(没有委托或基于资源的创作者)
我已经上传了所有源代码here。
有人可以帮我写一个反序列化器吗?
答案 0 :(得分:3)
一种有效的方法是编写您的自定义创建者:
@JsonCreator
public Property(JsonNode node) {
switch (node.getNodeType()) {
case STRING:
value = node.asText();
break;
case NUMBER:
value = node.asInt();
break;
case BOOLEAN:
value = node.asBoolean();
break;
case ARRAY:
Iterator<JsonNode> elements = node.elements();
List<String> list = new ArrayList<>();
while (elements.hasNext()) {
JsonNode element = elements.next();
if (element.isTextual()) {
list.add(element.textValue());
} else {
throw new RuntimeException("invalid data type");
}
}
value = list;
break;
case OBJECT:
try {
value = new ObjectMapper().treeToValue(node, Ref.class);
} catch (JsonProcessingException e) {
throw new RuntimeException("invalid data type", e);
}
break;
default:
throw new RuntimeException("invalid data type");
}
}
答案 1 :(得分:1)
这是我的解决方案,它使用Java反射将属性构造函数与json数据类型进行匹配。我必须修改属性构造函数以匹配Jackson的反序列化(使用包装器类型而不是基元)。另外,引用类型是从Map构建的(Reference
类已删除所有Jackson注释)
属性类:
import java.util.List;
import java.util.Map;
public class Property {
private Object value;
public Property(String value) {
this.value = value;
}
public Property(Integer value) {
this.value = value;
}
public Property(List<String> values) {
this.value = values;
}
public Property(Boolean value) {
this.value = value;
}
public Property(Ref reference) { // not used
this.value = reference;
}
// reference is received as map
public Property(Map<String, String> map) {
if (map.containsKey("Ref")) {
this.value = new Ref(map.get("Ref"));
} else {
throw new IllegalArgumentException("invalid reference property");
}
}
@Override
public String toString() {
return value.toString();
}
}
模板类
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class Template {
@JsonIgnore
public Map<String, Property> propertyList = new HashMap<>();
// map of data type from json to property constructor
private static Map<Class<?>, Constructor<Property>> propertyConstructors;
// build propertyConstructors map
static {
propertyConstructors = new HashMap<>();
@SuppressWarnings("unchecked")
Constructor<Property>[] constructors = (Constructor<Property>[])Property.class.getConstructors();
for (Constructor<Property> ctor : constructors) {
if (ctor.getParameterCount() == 1) {
propertyConstructors.put(ctor.getParameterTypes()[0], ctor);
}
}
}
@JsonProperty("Properties")
public void setProperties(Map<String, Object> jsonProperties) {
if (jsonProperties == null || jsonProperties.isEmpty()) return;
for (Map.Entry<String, Object> property : jsonProperties.entrySet()) {
Optional<Constructor<Property>> optionalCtor =
propertyConstructors.keySet().stream()
.filter(argType -> argType.isAssignableFrom(property.getValue().getClass()))
.map(argType -> propertyConstructors.get(argType))
.findFirst();
if (optionalCtor.isPresent()) {
try {
propertyList.put(property.getKey(), optionalCtor.get().newInstance(property.getValue()));
} catch (ReflectiveOperationException e) {
throw new IllegalArgumentException("invalid property " + property.getKey());
}
}
}
}
}
反序列化很简单:
Template t = mapper.readValue(in, Template.class);
答案 2 :(得分:0)
根据我的经验,杰克逊不喜欢整个List界面。
public class Property{
//...
public Property(String[] list){
this.value = list;
}
//...
或者如果您想使用列表对象:
public class Property{
//...
public Property(String[] list){
this.value = Arrays.asList(list);
}
//...
或简单地使用@cassiomolin的答案