在 Java 中使用 Jackson 进行地图反序列化
Posted
技术标签:
【中文标题】在 Java 中使用 Jackson 进行地图反序列化【英文标题】:Map deserializing with Jackson in Java 【发布时间】:2015-02-01 17:02:31 【问题描述】:我有以下课程
public class BetWrapper
private String description;
private Calendar startTime;
private HashMap<String, SimpleEntry<Integer, Double>> map;
public BetWrapper()
map = new HashMap<>();
public Calendar getStartTime()
return startTime;
public void setStartTime(Calendar startTime)
this.startTime = startTime;
public String getDescription()
return description;
public void setDescription(String description)
this.description = description;
public HashMap<String, SimpleEntry<Integer, Double>> getMap()
return map;
public void setMap(HashMap<String, SimpleEntry<Integer, Double>> map)
this.map = map;
我正在使用 JSONUtil 类
public class JSONUtil
private JSONUtil()
public static <T> T fromJSON(String content, Class<T> clazz) throws TechnicalException
try
return new ObjectMapper().readValue(content, clazz);
catch (IOException e)
throw new TechnicalException(e);
public static String toJSON(Object obj) throws TechnicalException
try
return new ObjectMapper().writeValueAsString(obj);
catch (JsonProcessingException ex)
throw new TechnicalException(ex);
我想将 JSON 反序列化为 BetWrapper 对象。但是下面的代码会产生一些异常。
BetWrapper betWrapper = new BetWrapper();
betWrapper.setDescription("Stoke City - Arsenal");
betWrapper.setStartTime(Calendar.getInstance());
HashMap<String, AbstractMap.SimpleEntry<Integer, Double>> map = new HashMap<>();
map.put("home_team", new AbstractMap.SimpleEntry<>(1, 2.85));
betWrapper.setMap(map);
String json = JSONUtil.toJSON(betWrapper);
JSONUtil.fromJSON(json, BetWrapper.class);
例外情况是:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class java.util.AbstractMap$SimpleEntry<java.lang.Integer,java.lang.Double>]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: "description":"Stoke City - Arsenal","startTime":1417648132139,"map":"home_team":"key":1,"value":2.85; line: 1, column: 85] (through reference chain: by.bsu.kolodyuk.bettingapp.model.entity.BetWrapper["map"])
如何正确反序列化?似乎问题在于 SimpleEntry 中的类型 K,V 应该以某种方式为 Jackson 指定。
有什么想法吗?
【问题讨论】:
我们来看看SimpleEntry
。
SimpleEntry 是 AbstractMap 的静态内部类
***.com/questions/7625783/…
【参考方案1】:
SimpleEntry
类型有如下构造函数
public SimpleEntry(K key, V value)
this.key = key;
this.value = value;
默认情况下,Jackson 需要一个无参数的构造函数。如果这样的构造函数不存在,它会查找带有@JsonProperty
注释的构造函数。 (我可能有这个倒退,但永远不要这样编码。)因为你正在使用 JDK 类型,它显然不会有那些注释。
您可以使用 Mixin。 Jackson 使用这些作为模板来反序列化您的目标类型。
abstract class Mixin<K, V>
public Mixin(@JsonProperty("key") K key, @JsonProperty("value") V value)
...
public static <T> T fromJSON(String content, Class<T> clazz) throws Exception
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(SimpleEntry.class, Mixin.class);
return mapper.readValue(content, clazz);
Jackson 将使用元类型 Mixin
,反序列化为 SimpleEntry
对象。
(注意,Mixin
的类型参数和构造函数参数类型并不重要。重要的是构造函数参数有两个,构造函数参数有注释。)
【讨论】:
正确,有一个小的改动:type
的构造函数参数确实很重要(名称不重要)。
@StaxMan 我昨天试过了,它没有抱怨。也许我没有做我认为的那样。让我再试一次。
@StaxMan 所以,是的。我也是那么想的。但是我只是为 mixin 构造函数参数设置了一些完全随机的类型,它仍然有效。
@SotiriosDelimanolis 很有趣。这实际上听起来像一个错误。谢谢!【参考方案2】:
在 Jackson 中,您可以定义自定义反序列化器。因此,对于您的情况,它可能如下所示:
import java.io.IOException;
import java.util.AbstractMap;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public class SomeDeserializer extends StdDeserializer<AbstractMap.SimpleEntry>
public SomeDeserializer()
super( AbstractMap.SimpleEntry.class );
@Override
public AbstractMap.SimpleEntry deserialize( JsonParser jp, DeserializationContext ctxt ) throws IOException, JsonProcessingException
Integer key = null;
Double value = null;
JsonToken token;
while ( ( token = jp.nextValue() ) != null )
if ( token.isNumeric() )
String propertyName = jp.getCurrentName();
if ( "key".equalsIgnoreCase( propertyName ) )
key = jp.getIntValue();
else if ( "value".equalsIgnoreCase( propertyName ) )
value = jp.getDoubleValue();
if ( key != null && value != null )
return new AbstractMap.SimpleEntry( key, value );
return null;
应使用ObjectMapper.registerModule(Module m)
注册反序列化程序。在您的情况下,您可以在 JSONUtil
实用程序类中执行此操作:
SimpleModule module = new SimpleModule();
module.addDeserializer( AbstractMap.SimpleEntry.class, new SomeDeserializer() );
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule( module );
请注意,反序列化器是使用ObjectMapper
实例注册的。因此,您最好将实例存储为实用程序类的字段。
上面的反序列化器类不全面!这只是为了演示手头的案例。可以根据需要应用进一步的优化和重构。
【讨论】:
以上是关于在 Java 中使用 Jackson 进行地图反序列化的主要内容,如果未能解决你的问题,请参考以下文章