具有后退到默认序列化的自定义序列化器

问题描述 投票:0回答:2

我有一个对象可能需要以不同方式序列化的情况。具体来说,一个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...
    }
}

并且要序列化的类将相应地进行注释:

@JsonSerialize(using = FooSerializer.class)
public class Foo
{
    // ... things...
}

问题几乎立即变得很明显:jsonGenerator.writeObject调用ObjectMapper来序列化对象...然后将再次调用我的序列化器,这给了我们一个可爱的无限循环,并最终导致了堆栈溢出。

我能想到的解决此问题的唯一方法是使用反射,遍历对象的属性,然后使用jsonGenerator将其写入新对象。这感觉有点过头了,特别是当杰克逊应该能够为我做的时候。问题是我找不到告诉ObjectMapper忽略类上的序列化程序的方法。

是否有更好的方法?我在想我可以创建一个自定义的ObjectMapper,以某种方式忽略类上标注的序列化程序,并将其用作jsonGenerator的编解码器...但不能完全确定要使用哪种杠杆来控制ObjectMapper

java serialization jackson
2个回答
0
投票

怀疑,有一种方法可以禁用特定注释。在这种情况下,我想在JsonSerialize类上禁用Foo批注。因此,我这样做是为了打破无限循环:

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

0
投票

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

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

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

现在,我们可以通过实现Flattenable接口轻松处理其他类型。序列化逻辑将是相同的。自定义序列化器可能如下所示:

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

[当我们有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;
    }
}

上面的代码打印两行:

//1
{"id":1,"random":0.7818309762014325}

//2
{"flattened":false,"id":2}
© www.soinside.com 2019 - 2024. All rights reserved.