防止 Jackson ObjectMapper 自动以 String() 格式格式化复合映射键
Prevent Jackson ObjectMapper automatically toString()-formatting composite map keys
我正在使用 com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString()
从 Java POJO 生成 JSON。
我的一个对象 - 让我们称之为 ResultSheet
- 包含一个 属性 Map<ResultId,Integer> results
。 ResultId
表示复合 PK(两个 FK)。
似乎 ObjectMapper
在遇到 ResultId
时意识到这不能直接在 JSON 中表示,因为 JSON 映射键必须是字符串。因此它输出的是 toString()
of ResultId
。 (在这种特殊情况下,这会产生一个 "42:43"
形式的字符串,但这只是因为这恰好是 toString()
当前的实现方式。)
我怎样才能阻止它这样做?当前的行为隐藏了这样一个事实,即被编组的结构不能正确表示为 JSON 结构。它还将当前 toString()
实现泄漏到我的网络服务的 API 中。
我宁愿抛出异常告诉我用作映射键的值何时不是 String
- 或者至少,当转换为 String
并非易事时. (例如,我不太担心 Long
键被 toString()
编辑。)
我查看了 SerializationFeature
设置,但其中 none 似乎解决了这个问题。 https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/SerializationFeature.html
有什么办法可以避免这种情况吗?能否以一般方式完成,即不仅针对这个特定的 class,而且在未来可能出现的任何情况下,作为新的 classes,其中一些可能用作映射键,是已添加?
请注意,在这种情况下,使 toString()
抛出异常是不可接受的。
要解决此问题,您可以实施并注册自己的 com.fasterxml.jackson.databind.ser.BeanSerializerModifier
class。并覆盖 modifyKeySerializer 方法。实现总是可以 return 一个抛出异常的序列化程序。请参阅以下示例:
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)
我正在使用 com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString()
从 Java POJO 生成 JSON。
我的一个对象 - 让我们称之为 ResultSheet
- 包含一个 属性 Map<ResultId,Integer> results
。 ResultId
表示复合 PK(两个 FK)。
似乎 ObjectMapper
在遇到 ResultId
时意识到这不能直接在 JSON 中表示,因为 JSON 映射键必须是字符串。因此它输出的是 toString()
of ResultId
。 (在这种特殊情况下,这会产生一个 "42:43"
形式的字符串,但这只是因为这恰好是 toString()
当前的实现方式。)
我怎样才能阻止它这样做?当前的行为隐藏了这样一个事实,即被编组的结构不能正确表示为 JSON 结构。它还将当前 toString()
实现泄漏到我的网络服务的 API 中。
我宁愿抛出异常告诉我用作映射键的值何时不是 String
- 或者至少,当转换为 String
并非易事时. (例如,我不太担心 Long
键被 toString()
编辑。)
我查看了 SerializationFeature
设置,但其中 none 似乎解决了这个问题。 https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/SerializationFeature.html
有什么办法可以避免这种情况吗?能否以一般方式完成,即不仅针对这个特定的 class,而且在未来可能出现的任何情况下,作为新的 classes,其中一些可能用作映射键,是已添加?
请注意,在这种情况下,使 toString()
抛出异常是不可接受的。
要解决此问题,您可以实施并注册自己的 com.fasterxml.jackson.databind.ser.BeanSerializerModifier
class。并覆盖 modifyKeySerializer 方法。实现总是可以 return 一个抛出异常的序列化程序。请参阅以下示例:
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)