使用已配置的Jackson ObjectMapper发生意外的序列化行为 [英] Unexpected serialization behavior with configured Jackson ObjectMapper
问题描述
我正在使用Jackson 2.10.5序列化相同的java.util.Date
对象3次.
I am using Jackson 2.10.5 to serialize the same java.util.Date
object three times.
- 第一次使用基本的Jackson
ObjectMapper
.我看到了时间戳. - 然后我配置相同的
ObjectMapper
,然后重写.我得到相同的结果. - 然后我构造一个新的
ObjectMapper
,以相同的方式配置它.我得到了不同的结果,即JSON列表中的类名和时间戳.
- The first time, with a basic Jackson
ObjectMapper
. I see the timestamp. - Then I configure the same
ObjectMapper
, and rewrite. I get the same result. - Then I construct a new
ObjectMapper
, configure it the same way. I get a different result, the class name and the timestamp in a JSON list.
该配置旨在告知ObjectMapper
将除java.util.Date
之外的每个对象的类名称作为JSON属性包含.
The configuration is intended to tell the ObjectMapper
to include the class name of every object except java.util.Date
as a JSON property.
所以我有两个问题.首先是为什么在第三种情况下日期对象的序列化有所不同?有关以不同方式使用PolymorphicTypeMapper
的任何建议,将不胜感激.
So I have two questions. The first is why is the date object serialized differently in the third case? Any advice on using the PolymorphicTypeMapper
differently would be appreciated.
第二个是为什么第一个和第二个相同的原因(是因为对象映射器具有缓存(哎呀!)吗?).
The second is why the first and second are the same (is it because the Object mapper has a cache (ouch!)?).
private PolymorphicTypeValidator getPTV() {
return BasicPolymorphicTypeValidator.builder()
.denyForExactBaseType(Date.class)
.build();
}
@Test
public void serializationTest() {
try {
Date now = new Date();
// Create an object mapper and serialize the date
ObjectMapper om = new ObjectMapper();
String serialized1 = om.writeValueAsString(now); // result: 1605744866827
om.activateDefaultTypingAsProperty(getPTV(), ObjectMapper.DefaultTyping.EVERYTHING, "@class");
String serialized2 = om.writeValueAsString(now); // result: 1605744866827
ObjectMapper om2 = new ObjectMapper();
om2.activateDefaultTypingAsProperty(getPTV(), ObjectMapper.DefaultTyping.EVERYTHING, "@class");
String serialized3 = om2.writeValueAsString(now); // result: ["java.util.Date",1605744866827]
Logger.getLogger(SerializationTest.class).info(serialized1);
Logger.getLogger(SerializationTest.class).info(serialized2);
Logger.getLogger(SerializationTest.class).info(serialized3);
Assert.assertEquals("Unexpected change in serialization 1-2", serialized1, serialized2);
Assert.assertEquals("Unexpected change in serialization 1-3", serialized1, serialized3);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
这是输出:
INFO 2020-11-18 16:14:27,065 [main] <> test.SerializationTest : 1605744866827
INFO 2020-11-18 16:14:27,066 [main] <> test.SerializationTest : 1605744866827
INFO 2020-11-18 16:14:27,066 [main] <> test.SerializationTest : ["java.util.Date",1605744866827]
org.junit.ComparisonFailure: Unexpected change in serialization 1-3
Expected :1605744866827
Actual :["java.util.Date",1605744866827]
推荐答案
Read the documentation, i.e. the javadoc of ObjectMapper
, which says (bold highlights by me):
映射器实例是完全线程安全的,前提是实例的 ALL配置在任何读取或写入调用之前发生.如果首次使用后修改了映射器的配置,则更改可能会生效或可能不会生效,并且配置调用自身可能会失败.如果需要使用其他配置,则有两种主要可能性:
Mapper instances are fully thread-safe provided that ALL configuration of the instance occurs before ANY read or write calls. If configuration of a mapper is modified after first usage, changes may or may not take effect, and configuration calls themselves may fail. If you need to use different configuration, you have two main possibilities:
-
构造并使用
ObjectReader
进行阅读,使用ObjectWriter
进行书写.两种类型都是完全不变的,您可以使用ObjectMapper
的工厂方法或读取器/写入器本身自由创建具有不同配置的新实例.构造新的ObjectReader
和ObjectWriter
的操作非常轻巧,因此通常需要根据需要在每次调用的基础上创建它们,以配置诸如JSON的可选缩进之类的内容.
Construct and use
ObjectReader
for reading,ObjectWriter
for writing. Both types are fully immutable and you can freely create new instances with different configuration using either factory methods ofObjectMapper
, or readers/writers themselves. Construction of newObjectReader
s andObjectWriter
s is a very light-weight operation so it is usually appropriate to create these on per-call basis, as needed, for configuring things like optional indentation of JSON.
如果通过ObjectReader
和ObjectWriter
无法获得特定类型的可配置性,则可能需要使用多个ObjectMapper
(例如:您不能更改混合-in即时注释;或者,一组自定义(反)序列化器).为了帮助使用此方法,您可能需要使用方法copy()
,该方法将创建具有特定配置的映射器的副本,并允许在使用复制实例之前对其进行配置.请注意,copy()
操作与构造一个新的ObjectMapper
实例一样昂贵:如果可能的话,如果打算将它们用于多个操作,则仍应合并并重用映射器.
If the specific kind of configurability is not available via ObjectReader
and ObjectWriter
, you may need to use multiple ObjectMapper
instead (for example: you can not change mix-in annotations on-the-fly; or, set of custom (de)serializers). To help with this usage, you may want to use method copy()
which creates a clone of the mapper with specific configuration, and allows configuration of the copied instance before it gets used. Note that copy()
operation is as expensive as constructing a new ObjectMapper
instance: if possible, you should still pool and reuse mappers if you intend to use them for multiple operations.
这篇关于使用已配置的Jackson ObjectMapper发生意外的序列化行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!