阻止Jackson ObjectMapper自动为String()格式化复合映射键



我使用com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString()从Java POJO生成JSON。

我的一个对象——让我们称之为ResultSheet——包含一个属性Map<ResultId,Integer> resultsResultId表示(两个FK的(复合PK。

似乎ObjectMapper在遇到ResultId时意识到这不能直接用JSON表示,因为JSON映射键必须是字符串。因此,它转而输出ResultIdtoString()。(在这种特殊情况下,这会产生一个形式为"42:43"的字符串,但这只是因为toString()目前正是这样实现的。(

我怎么能阻止它这么做?当前的行为隐藏了这样一个事实,即正在编组的结构不能正确地表示为JSON结构。它还将当前的toString()实现泄漏到我的web服务的API中。

我宁愿抛出一个异常,告诉我当用作映射键的值不是String时,或者至少当转换为String并非易事时。(例如,我不太关心Long密钥是toString()-ed。(

我已经研究了SerializationFeature设置,但它们似乎都没有解决这个问题。https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/SerializationFeature.html

有办法防止这种情况发生吗?它能以一种通用的方式完成吗?即不仅针对这个特定的类,而且在未来可能出现这种情况的任何情况下,因为添加了新的类,其中一些可能被用作映射键?

注意,在这种情况下,让toString()抛出异常是不可接受的。

要解决这个问题,您可以实现并注册自己的com.fasterxml.jackson.databind.ser.BeanSerializerModifier类。并重写modifyKeySerializer方法。实现可能总是返回一个引发异常的串行器。参见以下示例:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
public class JsonKeySerializerApp {
public static void main(String[] args) throws IOException {
SimpleModule keyExceptionSerializerModule = new SimpleModule();
keyExceptionSerializerModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifyKeySerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
// specify classes you want to allow to be serialized as keys
if (valueType.getRawClass().getPackage().getName().startsWith("java.")) {
return serializer;
}
return new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) {
throw new UnsupportedOperationException("You can not serialize POJO as keys!");
}
};
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(keyExceptionSerializerModule);
Map<ResultId, Long> map = Collections.singletonMap(new ResultId(1, 2), 1L);
mapper.writeValue(System.out, map);
}
}
@AllArgsConstructor
@Data
class ResultId {
private int id1;
private int id2;
}

以上代码打印:

{Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: You can not serialize POJO as keys! (through reference chain: java.util.Collections$SingletonMap["ResultId(id1=1, id2=2)"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:725)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:643)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)

最新更新