问题描述
我希望在对象序列化之前对其进行修改.我想编写一个自定义序列化器来解析对象,然后将其传递给默认的对象序列化器.
I am looking to modify an object right before it gets serialized. I want to write a custom serializer to parse the object, then pass it to the default object serializer.
这就是我所拥有的:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
*
* @author Me
*/
public class PersonSerializer extends JsonSerializer<Person>{
@Override
public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
//This returns a modified clone of Person value.
Person safePerson = PrivacyService.getSafePerson(value);
provider.defaultSerializeValue(safePerson, jgen);
}
}
但这只是无限循环.我也试过:
But that just goes in an infinate loop. I have also tried:
provider.findTypedValueSerializer(Person.class, true, null).serialize(safePerson, jgen, provider);
这可行,但它不会解析对象中的任何字段.
That works, but it doesn't parse any of the fields in the object.
我也尝试过使用 @JsonFilter
但它非常笨重并且使我的加载时间增加了六倍.
I also tried using a @JsonFilter
but it was extremely heavy and sextupled my load times.
帮助!谢谢!
推荐答案
虽然我最初很高兴找到@Nitroware 的答案,但不幸的是它在 Jackson 2.7.2 中不起作用 - BeanSerializerFactory.instance.createSerializer
再次反省Person
类上的JsonSerializer
注解,导致无限递归和StackOverflowError
.
Although I initially rejoiced over finding the @Nitroware's answer, it unfortunately does not work in Jackson 2.7.2 - BeanSerializerFactory.instance.createSerializer
introspects JsonSerializer
annotation on Person
class again, which leads to infinite recursion and StackOverflowError
.
如果 @JsonSerializer
在 POJO 类中不存在,将创建默认序列化器的点是 BeanSerializerFactory.constructBeanSerializer
方法.那么我们就直接使用这个方法吧.由于该方法是protected
,我们通过工厂子类使其可见,并为其提供有关序列化类的信息.此外,我们将不推荐使用的 SimpleType.construct
方法替换为其推荐的替换方法.整个解决方案是:
The point where default serializer would be created if @JsonSerializer
were absent on POJO class is the BeanSerializerFactory.constructBeanSerializer
method. So let's just use this method directly. Since the method is protected
, we make it visible via factory subclass and feed it with information about serialized class. Also, we replace deprecated SimpleType.construct
method by its recommended replacement. Whole solution is:
public class PersonSerializer extends JsonSerializer<PersonSerializer> {
static class BeanSerializerFactoryWithVisibleConstructingMethod extends BeanSerializerFactory {
BeanSerializerFactoryWithVisibleConstructingMethod() {
super(BeanSerializerFactory.instance.getFactoryConfig());
}
@Override
public JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov, BeanDescription beanDesc) throws JsonMappingException {
return super.constructBeanSerializer(prov, beanDesc);
}
}
private final BeanSerializerFactoryWithVisibleConstructingMethod defaultBeanSerializerFactory = new BeanSerializerFactoryWithVisibleConstructingMethod();
private final JavaType javaType = TypeFactory.defaultInstance().constructType(Person.class);
@Override
public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
Person safePerson = PrivacyService.getSafePerson(value);
JavaType type = TypeFactory.defaultInstance().constructType(Person.class);
BeanDescription beanDescription = provider.getConfig().introspect(type);
JsonSerializer<Object> defaultSerializer = defaultBeanSerializerFactory.constructBeanSerializer(provider, beanDescription);
defaultSerializer.serialize(safePerson, jgen, provider);
}
}
与基于 BeanSerializerModifier
的解决方案不同,您被迫在自定义序列化程序之外声明和注册特殊行为,此解决方案的特殊逻辑仍封装在自定义 PersonSerializer
中.
Unlike BeanSerializerModifier
-based solution where you are forced to declare and register special behaviour outside your custom serializer, with this solution special logic is still encapsulated in custom PersonSerializer
.
最终可能会将逻辑推送到自定义 DefaultJsonSerializerAware
祖先.
Eventually the logic might be pushed up to custom DefaultJsonSerializerAware
ancestor.
更新 2017-09-28:
我在上述推理中发现了错误.仅使用 BeanSerializerFactory.constructBeanSerializer
方法是不够的.如果原始类包含空字段,则它们不在输出中.(原因是 constructBeanSerializer
方法是从 createAndCacheUntypedSerializer
方法间接调用的,该方法后来调用了 addAndResolveNonTypedSerializer
方法,其中 NullSerializer
是添加到 BeanPropertyWriter
s 中.)
I found bug in reasoning stated above. Using sole BeanSerializerFactory.constructBeanSerializer
method is not enough. If original class contains null fields, they are not in output. (The reason is the constructBeanSerializer
method is indirectly called from createAndCacheUntypedSerializer
method which later calls addAndResolveNonTypedSerializer
method where NullSerializer
s are added into BeanPropertyWriter
s).)
这个问题的解决方案对我来说似乎是正确的并且非常简单,重用所有序列化逻辑,而不仅仅是constructBeanSerializer方法.这个逻辑从提供者的 serializeValue
方法开始.唯一不合适的是自定义 JsonSerialize
注释.因此,我们重新定义 BeanSerializationFactory
以假装自省的类(只有它 - 否则字段类型的 JsonSerialize 注释将不适用)没有 JsonSerialize
注释.
Solution to this problem which seems correct to me and is quite simple is to reuse all serialization logic, not only constructBeanSerializer method. This logic starts in provider's serializeValue
method. The only inappropriate thing is custom JsonSerialize
annotation. So we redefine BeanSerializationFactory
to pretend the introspected class (and only it - otherwise JsonSerialize annotations on field types would not apply) has no JsonSerialize
annotation.
@Override
public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
Person safePerson = PrivacyService.getSafePerson(value);
ObjectMapper objectMapper = (ObjectMapper)jgen.getCodec();
Class<?> entityClass = value.getClass();
JavaType javaType = TypeFactory.defaultInstance().constructType(entityClass);
DefaultSerializerProvider.Impl defaultSerializerProvider = (DefaultSerializerProvider.Impl) objectMapper.getSerializerProviderInstance();
BeanSerializerFactory factoryIgnoringCustomSerializerOnRootClass = new BeanSerializerFactory(BeanSerializerFactory.instance.getFactoryConfig()) {
@Override
protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov, Annotated a) throws JsonMappingException {
JsonSerializer<Object> result = javaType.equals(a.getType()) ? null : super.findSerializerFromAnnotation(prov, a);
return result;
}
};
DefaultSerializerProvider.Impl updatedSerializerProvider = defaultSerializerProvider.createInstance(defaultSerializerProvider.getConfig(), factoryIgnoringCustomSerializerOnRootClass);
updatedSerializerProvider.serializeValue(jgen, value);
}
请注意,如果您没有遇到空值问题,那么以前的解决方案就足够了.
Note if you don't suffer with problem with nulls, previous solution is enough for you.
这篇关于Jackson JSON 在序列化之前修改对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!