Java 具有回退到默认序列化的自定义序列化程序

Java 具有回退到默认序列化的自定义序列化程序,java,serialization,jackson,Java,Serialization,Jackson,我有一个例子,对象可能需要以不同的方式序列化。具体地说,REST端点接受一个标头,如果标头为true,我们将以一种方式序列化,如果为false,我们将以另一种方式序列化。我认为如果我们将header属性放在对象中,那么定制序列化程序可能非常适合这种情况,但是我在使用它时遇到了一些麻烦 (请注意,由于使用了一些泛型以及代码的结构方式,仅仅创建一个不同的对象就需要大量的重新分解,因此我正在尝试找到一个更快的解决方案。) 例如,我的序列化程序如下所示: public class FooSerializ

我有一个例子,对象可能需要以不同的方式序列化。具体地说,REST端点接受一个标头,如果标头为true,我们将以一种方式序列化,如果为false,我们将以另一种方式序列化。我认为如果我们将header属性放在对象中,那么定制序列化程序可能非常适合这种情况,但是我在使用它时遇到了一些麻烦

(请注意,由于使用了一些泛型以及代码的结构方式,仅仅创建一个不同的对象就需要大量的重新分解,因此我正在尝试找到一个更快的解决方案。)

例如,我的序列化程序如下所示:

public class FooSerializer extends StdSerializer<Foo>
{
    @Override
    public void serialize(
        final Foo foo,
        final JsonGenerator jsonGenerator,
        final SerializerProvider serializerProvider)
        throws IOException
    {
        if (foo.isFlattened())
        {
            jsonGenerator.writeObject(flatten(foo));
        }
        else
        {
            jsonGenerator.writeObject(foo);
        }
    }

    private Map<String,Object> flatten(Foo foo)
    {
        // ... some logic that builds the Map...
    }
}
问题几乎立即变得显而易见:
jsonGenerator.writeObject
调用
ObjectMapper
来序列化对象。。。这将再次调用我的序列化程序,最终给我们一个可爱的无限循环和堆栈溢出

我能想到的解决这个问题的唯一方法是使用反射,循环对象的属性,并使用
jsonggenerator
将它们写入新对象。这感觉太过分了,尤其是当杰克逊能够为我做到这一点的时候。问题是我找不到方法告诉
ObjectMapper
忽略类上的序列化程序


有更好的方法吗?我想我可以创建一个自定义的
ObjectMapper
,它以某种方式忽略类上注释的序列化程序,并将其用作
jsonGenerator
的编解码器。。。但是不能完全确定在
对象映射器上拉什么样的杠杆,正如怀疑的那样,有一种方法可以禁用特定的注释。在本例中,我想禁用
Foo
类上的
JsonSerialize
注释。所以,我这样做是为了打破无限循环:

public class FooSerializer extends StdSerializer<Foo>
{
    @Override
    public void serialize(
        final Foo foo,
        final JsonGenerator jsonGenerator,
        final SerializerProvider serializerProvider)
        throws IOException
    {
        ObjectMapper mapper = new ObjectMapper();
        JacksonAnnotationIntrospector annotationIntrospector =
            new JacksonAnnotationIntrospector()
        {
            @Override
            protected <A extends Annotation> A _findAnnotation(
                final Annotated annotated,
                final Class<A> annotationClass)
            {
                if (annotated.hasAnnotation(JsonSerialize.class) &&
                    annotated.getRawType() == Foo.class)
                {
                    return null;
                }

                return super._findAnnotation(annotated, annotationClass);
            }
        };
        mapper.setAnnotationIntrospector(annotationIntrospector);
        jsonGenerator.setCodec(mapper);

        if (foo.isFlattened())
        {
            jsonGenerator.writeObject(flatten(foo));
        }
        else
        {
            jsonGenerator.writeObject(foo);
        }
    }

    private Map<String,Object> flatten(Foo foo)
    {
        // ... some logic that builds the Map...
    }
}
公共类FooSerializer扩展StdSerializer
{
@凌驾
公共无效序列化(
最后的福福,
最终JsonGenerator JsonGenerator,
最终序列化提供程序(序列化提供程序)
抛出IOException
{
ObjectMapper mapper=新的ObjectMapper();
JacksonAnnotation内省者Annotation内省者=
新JacksonAnnotationInterspector()
{
@凌驾
受保护的A_findAnnotation(
最终注释,
最终类注释(类)
{
if(annotated.hasnotation(JsonSerialize.class)&&
注释的.getRawType()==Foo.class)
{
返回null;
}
返回super.\u findAnnotation(注释,annotationClass);
}
};
mapper.SetAnnotationIntroSector(注释IntroSector);
setCodec(映射器);
if(foo.isplatten())
{
writeObject(flatte(foo));
}
其他的
{
jsonggenerator.writeObject(foo);
}
}
私人地图展平(Foo-Foo)
{
//…一些构建地图的逻辑。。。
}
}

Jackson
允许以多种不同的方式注册自定义序列化程序。其中之一是使用
com.fasterxml.jackson.databind.ser.BeanSerializerModifier
类,该类允许创建自定义序列化程序,但如果需要,我们也可以使用那里提供的基本序列化程序

上面的问题看起来像是
OOP
问题的一个很好的例子,我们可以将
POJO
解耦,这是逻辑和序列化过程。Serialiser不应该知道如何将对象转换为
Map
。它应该只有序列化逻辑。让我们介绍一下
POJO
和serialiser使用的接口:

interface Flattenable {

    boolean isFlattened();

    Map<String, Object> flatten();
}

class Foo implements Flattenable {

    private boolean flattened;
    private int id;

    public Foo(boolean flattened, int id) {
        this.flattened = flattened;
        this.id = id;
    }

    @Override
    public Map<String, Object> flatten() {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", getId());
        map.put("random", ThreadLocalRandom.current().nextDouble());

        return map;
    }

    @Override
    public boolean isFlattened() {
        return flattened;
    }

    public void setFlattened(boolean flattened) {
        this.flattened = flattened;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
当我们有
POJO
模型和序列化器时,我们只需要配置
ObjectMapper
并尝试使用我们的解决方案:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
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.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

public class JsonFlattenApp {

    public static void main(String[] args) throws Exception {
        SimpleModule flattenModule = new SimpleModule("FlattenModule");
        flattenModule.setSerializerModifier(new FlattenableBeanSerializerModifier());

        ObjectMapper mapper = JsonMapper.builder()
                .addModule(flattenModule)
                .build();

        System.out.println(mapper.writeValueAsString(new Foo(true, 1)));
        System.out.println(mapper.writeValueAsString(new Foo(false, 2)));
    }
}

class FlattenableBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        if (Flattenable.class.isAssignableFrom(beanDesc.getBeanClass())) {
            return new FlattenableJsonSerializer(serializer);
        }

        return serializer;
    }
}

您的解决方案看起来有点像黑客。对于每个序列化过程,它都将创建新的
ObjectMapper
,并自定义实现
JacksonAnnotationIntrospector
,从而禁用
Foo
的自定义序列化程序。另外,通过
jsonGenerator.setCodec(mapper)
覆盖编解码器可能有缺点,因为该过程中的所有其他对象都将由
ObjectMapper
的此实例序列化。如果您需要为
java8
java8 Time
等类使用一些自定义或通用模块,那么您每次都必须在自定义序列化程序中重新注册它们。考虑使用矿山解决方案。
class FlattenableJsonSerializer extends JsonSerializer<Flattenable> {

    private final JsonSerializer<Object> base;

    public FlattenableJsonSerializer(JsonSerializer base) {
        this.base = Objects.requireNonNull(base);
    }

    @Override
    public void serialize(Flattenable value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value.isFlattened()) {
            gen.writeObject(value.flatten());
        } else {
            base.serialize(value, gen, serializers);
        }
    }
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
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.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

public class JsonFlattenApp {

    public static void main(String[] args) throws Exception {
        SimpleModule flattenModule = new SimpleModule("FlattenModule");
        flattenModule.setSerializerModifier(new FlattenableBeanSerializerModifier());

        ObjectMapper mapper = JsonMapper.builder()
                .addModule(flattenModule)
                .build();

        System.out.println(mapper.writeValueAsString(new Foo(true, 1)));
        System.out.println(mapper.writeValueAsString(new Foo(false, 2)));
    }
}

class FlattenableBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        if (Flattenable.class.isAssignableFrom(beanDesc.getBeanClass())) {
            return new FlattenableJsonSerializer(serializer);
        }

        return serializer;
    }
}
//1
{"id":1,"random":0.7818309762014325}

//2
{"flattened":false,"id":2}