本文介绍了Jackson JSON 在序列化之前修改对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望在对象序列化之前对其进行修改.我想编写一个自定义序列化器来解析对象,然后将其传递给默认的对象序列化器.

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 是添加到 BeanPropertyWriters 中.)

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 NullSerializers are added into BeanPropertyWriters).)

这个问题的解决方案对我来说似乎是正确的并且非常简单,重用所有序列化逻辑,而不仅仅是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 在序列化之前修改对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 22:56
查看更多