ContextualDeserializer用于将JSON映射到与Jackson不同类型的地图 [英] ContextualDeserializer for mapping JSON to different types of maps with Jackson
问题描述
这个JSON片段应该被映射到一个Java对象,该对象包含 Map< String,Car> $ c
自行车
类型的字段 Map< String,Bike>
。因为自行车和汽车可以在JSON文件中为空字符串,所以我需要一个自定义的反序列化器(
{
id :1234,
name:John Doe,
cars:{
Tesla Model S:{
color:silver,
buying_date:2012-06-01
},
Toyota Yaris:{
color:blue,
buying_date :2005-01-01
}
},
自行车:{
Bike 1:{
color:black$ b $ b,
Bike 2:{
color:red
}
}
}
$ c $我想过有一个可以由 createContextual(DeserializationConfig cfg,BeanProperty属性)返回的泛型自定义反序列化器的实例,
基于BeanProperty参数的 ContextualDeserializer
的方法。通用自定义反序列化器看起来像这样:
public class MapsGenericDeserializer< T>扩展
JsonDeserializer< Map< String,T>> {
私有ObjectMapper映射器; //无需特殊映射反序列化器的ObjectMapper
public MapsGenericDeserializer(ObjectMapper映射器){
this.mapper = mapper;
}
@Override
public Map< String,T> deserialize(JsonParser jp,DeserializationContext ctxt)
抛出IOException,JsonProcessingException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if(!。equals(node.getTextValue())){
return mapper.readValue(node,
new TypeReference< Map< String,T>>(){} );
}
返回null; // Node是一个空字符串
}
}
下面的上下文序列化程序从 MapsGenericDeserializer< Car>
转换为 JsonDeserializer< Map< String,?>>
不适用可能。也许这在Java的新版本中是可行的,但它不适用于我编码的Android版本。所以我怎么能实现所需的行为?
public class MapsDeserializer扩展了JsonDeserializer< Map< String,?>>
实现了ContextualDeserializer< Map< String,?>>> {
私有ObjectMapper映射器;
MapsGenericDeserializer< Car> carDeserializer = new MapsGenericDeserializer< Car>(mapper);
MapsGenericDeserializer< Bike> bikeDeserializer = new MapsGenericDeserializer< Bike>(mapper);
public MapsDeserializer(ObjectMapper映射器){
this.mapper = mapper;
}
@Override
public JsonDeserializer< Map< String,?>> createContextual(DeserializationConfig cfg,
BeanProperty属性)抛出JsonMappingException {
Class<?> targetClass = property.getType()。containedType(1).getRawClass();
if(targetClass.equals(Car.class){
return carDeserializer; // Type mismatch!
} else if(targetClass.equals(Bike.class)){
return bikeDeserializer; //类型不匹配!
} else {
return this;
}
}
// ...
解决方案以下是我可能会遇到的问题。 / p>
import java.io.File;
import java.io.IOException;
import java.util。 HashMap;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org。 codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.ContextualDeserializer;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializationCo ntext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
public class Foo
{
public static void main(String [] args)throws Exception
{
EmptyStringAsMapDeserializer< Map< String,?> > emptyStringAsMapDeserializer =
EmptyStringAsMapDeserializer< Map< String,?>>(null,new ObjectMapper());
SimpleModule module = new SimpleModule(ThingsDeserializer,Version.unknownVersion());
module.addDeserializer(Map.class,emptyStringAsMapDeserializer);
ObjectMapper mapper = new ObjectMapper()。withModule(module);
Person person = mapper.readValue(new File(input.json),Person.class);
System.out.println(mapper.writeValueAsString(person));
}
}
class Person
{
public int id;
公共字符串名称;
public Map< String,Car>汽车;
public Map< String,Bike>自行车;
}
class Car
{
public String color;
public String buying_date;
}
class Bike
{
public String color;
}
class EmptyStringAsMapDeserializer< T>
扩展了JsonDeserializer< Map< String,?>>
实现了ContextualDeserializer< Map< String,?>>>
{
private Class<?> TARGETTYPE;
私人ObjectMapper映射器;
EmptyStringAsMapDeserializer(Class<?> targetType,ObjectMapper mapper)
{
this.targetType = targetType;
this.mapper = mapper;
}
@Override
public JsonDeserializer< Map< String,?>> createContextual(DeserializationConfig config,BeanProperty属性)
throws JsonMappingException
{
返回新EmptyStringAsMapDeserializer< Object>(property.getType()。containedType(1).getRawClass(),mapper);
}
@Override
public Map< String,?> deserialize(JsonParser jp,DeserializationContext ctxt)
抛出IOException异常,JsonProcessingException异常
{
JsonNode node = jp.readValueAsTree();
if(.equals(node.getTextValue()))
返回新的HashMap< String,Object>();
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(node,mapper.getTypeFactory()。constructMapType(Map.class,String.class,targetType));
泛型类型参数可能有点失序。我做了一些快速复制粘贴。
This JSON snippet should be mapped to a Java-Objects that contains a cars
field of type Map<String, Car>
and a bikes
field of type Map<String, Bike>
. Because bikes and cars can be empty strings in the JSON file, i need a custom deserializer (See this question).
{
"id" : "1234",
"name" : "John Doe",
"cars" : {
"Tesla Model S" : {
"color" : "silver",
"buying_date" : "2012-06-01"
},
"Toyota Yaris" : {
"color" : "blue",
"buying_date" : "2005-01-01"
}
},
"bikes" : {
"Bike 1" : {
"color" : "black"
},
"Bike 2" : {
"color" : "red"
}
}
}
I thought about having instances of a generic custom deserializer that can be returned by the createContextual(DeserializationConfig cfg, BeanProperty property)
method of a ContextualDeserializer
based on the BeanProperty parameter. The generic custom deserializer looks like this:
public class MapsGenericDeserializer<T> extends
JsonDeserializer<Map<String, T>> {
private ObjectMapper mapper; // ObjectMapper without special map deserializer
public MapsGenericDeserializer(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public Map<String, T> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if (!"".equals(node.getTextValue())) {
return mapper.readValue(node,
new TypeReference<Map<String, T>>() {});
}
return null; // Node was an empty string
}
}
The contextual serializer below does not work because casting from MapsGenericDeserializer<Car>
to JsonDeserializer<Map<String,?>>
is not possible. Maybe this is possible in newer Versions of Java, but it does not work on the Version of Android I am coding for. So how can I implement the desired behaviour?
public class MapsDeserializer extends JsonDeserializer<Map<String, ?>>
implements ContextualDeserializer<Map<String, ?>> {
private ObjectMapper mapper;
MapsGenericDeserializer<Car> carDeserializer = new MapsGenericDeserializer<Car>(mapper);
MapsGenericDeserializer<Bike> bikeDeserializer = new MapsGenericDeserializer<Bike>(mapper);
public MapsDeserializer(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public JsonDeserializer<Map<String, ?>> createContextual(DeserializationConfig cfg,
BeanProperty property) throws JsonMappingException {
Class<?> targetClass = property.getType().containedType(1).getRawClass();
if(targetClass.equals(Car.class) {
return carDeserializer; // Type mismatch!
} else if (targetClass.equals(Bike.class)) {
return bikeDeserializer; // Type mismatch!
} else {
return this;
}
}
// ...
}
解决方案 Here's how I might approach it.
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.ContextualDeserializer;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
public class Foo
{
public static void main(String[] args) throws Exception
{
EmptyStringAsMapDeserializer<Map<String, ?>> emptyStringAsMapDeserializer =
new EmptyStringAsMapDeserializer<Map<String, ?>>(null, new ObjectMapper());
SimpleModule module = new SimpleModule("ThingsDeserializer", Version.unknownVersion());
module.addDeserializer(Map.class, emptyStringAsMapDeserializer);
ObjectMapper mapper = new ObjectMapper().withModule(module);
Person person = mapper.readValue(new File("input.json"), Person.class);
System.out.println(mapper.writeValueAsString(person));
}
}
class Person
{
public int id;
public String name;
public Map<String, Car> cars;
public Map<String, Bike> bikes;
}
class Car
{
public String color;
public String buying_date;
}
class Bike
{
public String color;
}
class EmptyStringAsMapDeserializer<T>
extends JsonDeserializer<Map<String, ?>>
implements ContextualDeserializer<Map<String, ?>>
{
private Class<?> targetType;
private ObjectMapper mapper;
EmptyStringAsMapDeserializer(Class<?> targetType, ObjectMapper mapper)
{
this.targetType = targetType;
this.mapper = mapper;
}
@Override
public JsonDeserializer<Map<String, ?>> createContextual(DeserializationConfig config, BeanProperty property)
throws JsonMappingException
{
return new EmptyStringAsMapDeserializer<Object>(property.getType().containedType(1).getRawClass(), mapper);
}
@Override
public Map<String, ?> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
JsonNode node = jp.readValueAsTree();
if ("".equals(node.getTextValue()))
return new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(node, mapper.getTypeFactory().constructMapType(Map.class, String.class, targetType));
}
}
The generic type parameters might be a little out of order. I did a bit of quick copy-pasting.
这篇关于ContextualDeserializer用于将JSON映射到与Jackson不同类型的地图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!