Jackson JSON在序列化之前修改对象

时间:2022-02-22 18:02:22

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.

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

This is what I have:

这就是我所拥有的:

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.

这可行,但它不解析对象中的任何字段。

I also tried using a @JsonFilter but it was extremely heavy and sextupled my load times.

我也试过使用@JsonFilter,但它非常沉重,并且加载了我的加载时间。

Help! Thanks!

帮帮我!谢谢!

3 个解决方案

#1


5  

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 *Error.

虽然我最初对找到@ Nitroware的答案感到高兴,但遗憾的是它在Jackson 2.7.2中不起作用 - BeanSerializerFactory.instance.createSerializer再次在Person类上内省JsonSerializer注释,这导致无限递归和*Error。

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:

如果POJO类上没有@JsonSerializer,则创建默认序列化程序的点是BeanSerializerFactory.constructBeanSerializer方法。所以我们直接使用这个方法吧。由于该方法受到保护,我们通过工厂子类使其可见,并向其提供有关序列化类的信息。此外,我们通过推荐的替换方法替换弃用的SimpleType.construct方法。整个解决方案是:

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);
    }

}

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.

与基于BeanSerializerModifier的解决方案不同,您必须在自定义序列化程序之外声明和注册特殊行为,使用此解决方案时,特殊逻辑仍然封装在自定义PersonSerializer中。

Eventually the logic might be pushed up to custom DefaultJsonSerializerAware ancestor.

最终逻辑可能被推到自定义DefaultJsonSerializerAware祖先。

UPDATE 2017-09-28:

更新2017-09-28:

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).)

我发现上面提到的推理错误。使用唯一的BeanSerializerFactory.constructBeanSerializer方法是不够的。如果原始类包含空字段,则它们不在输出中。 (原因是从createAndCacheUntypedSerializer方法间接调用constructBeanSerializer方法,该方法稍后调用addAndResolveNonTypedSerializer方法,其中NullSerializers被添加到BeanPropertyWriters中。)

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.

解决这个问题对我来说似乎是正确的并且非常简单,就是重用所有序列化逻辑,而不仅仅是constructBeanSerializer方法。此逻辑从提供程序的serializeValue方法开始。唯一不合适的是自定义JsonSerialize注释。因此,我们重新定义BeanSerializationFactory以假装内省类(并且只有它 - 否则J字节上的JsonSerialize注释将不适用)没有JsonSerialize注释。

@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.

请注意,如果您没有遇到空值问题,以前的解决方案就足够了。

#2


6  

Holy crap, after several hours of digging through this library, trying to write my own factory, and a thousand other things, I FINALLY got this stupid thing to do what I wanted:

神圣的废话,经过几个小时的挖掘这个图书馆,试图写我自己的工厂,以及其他一千个东西,我最终得到了这个愚蠢的事情做我想要的:

public class PersonSerializer extends JsonSerializer<Person>{

    @Override
    public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {

        Person safePerson = PrivacyService.getSafePerson(value);

        //This is the crazy one-liner that will save someone a very long time
        BeanSerializerFactory.instance.createSerializer(provider, SimpleType.construct(Person.class)).serialize(safePerson, jgen, provider);

    }

}

#3


6  

Since Jackson 2.2 one might use the converter in JsonSerialize annotation:

从Jackson 2.2开始,可以在JsonSerialize注释中使用转换器:

@JsonSerialize(converter = OurConverter.class)

@JsonSerialize(converter = OurConverter.class)

and the converter

和转换器

public class OurConverter extends StdConverter<IN, OUT>

公共类OurConverter扩展了StdConverter ,out>

IN and OUT are same class if modifying object

如果修改对象,IN和OUT是同一类

#1


5  

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 *Error.

虽然我最初对找到@ Nitroware的答案感到高兴,但遗憾的是它在Jackson 2.7.2中不起作用 - BeanSerializerFactory.instance.createSerializer再次在Person类上内省JsonSerializer注释,这导致无限递归和*Error。

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:

如果POJO类上没有@JsonSerializer,则创建默认序列化程序的点是BeanSerializerFactory.constructBeanSerializer方法。所以我们直接使用这个方法吧。由于该方法受到保护,我们通过工厂子类使其可见,并向其提供有关序列化类的信息。此外,我们通过推荐的替换方法替换弃用的SimpleType.construct方法。整个解决方案是:

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);
    }

}

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.

与基于BeanSerializerModifier的解决方案不同,您必须在自定义序列化程序之外声明和注册特殊行为,使用此解决方案时,特殊逻辑仍然封装在自定义PersonSerializer中。

Eventually the logic might be pushed up to custom DefaultJsonSerializerAware ancestor.

最终逻辑可能被推到自定义DefaultJsonSerializerAware祖先。

UPDATE 2017-09-28:

更新2017-09-28:

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).)

我发现上面提到的推理错误。使用唯一的BeanSerializerFactory.constructBeanSerializer方法是不够的。如果原始类包含空字段,则它们不在输出中。 (原因是从createAndCacheUntypedSerializer方法间接调用constructBeanSerializer方法,该方法稍后调用addAndResolveNonTypedSerializer方法,其中NullSerializers被添加到BeanPropertyWriters中。)

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.

解决这个问题对我来说似乎是正确的并且非常简单,就是重用所有序列化逻辑,而不仅仅是constructBeanSerializer方法。此逻辑从提供程序的serializeValue方法开始。唯一不合适的是自定义JsonSerialize注释。因此,我们重新定义BeanSerializationFactory以假装内省类(并且只有它 - 否则J字节上的JsonSerialize注释将不适用)没有JsonSerialize注释。

@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.

请注意,如果您没有遇到空值问题,以前的解决方案就足够了。

#2


6  

Holy crap, after several hours of digging through this library, trying to write my own factory, and a thousand other things, I FINALLY got this stupid thing to do what I wanted:

神圣的废话,经过几个小时的挖掘这个图书馆,试图写我自己的工厂,以及其他一千个东西,我最终得到了这个愚蠢的事情做我想要的:

public class PersonSerializer extends JsonSerializer<Person>{

    @Override
    public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {

        Person safePerson = PrivacyService.getSafePerson(value);

        //This is the crazy one-liner that will save someone a very long time
        BeanSerializerFactory.instance.createSerializer(provider, SimpleType.construct(Person.class)).serialize(safePerson, jgen, provider);

    }

}

#3


6  

Since Jackson 2.2 one might use the converter in JsonSerialize annotation:

从Jackson 2.2开始,可以在JsonSerialize注释中使用转换器:

@JsonSerialize(converter = OurConverter.class)

@JsonSerialize(converter = OurConverter.class)

and the converter

和转换器

public class OurConverter extends StdConverter<IN, OUT>

公共类OurConverter扩展了StdConverter ,out>

IN and OUT are same class if modifying object

如果修改对象,IN和OUT是同一类