自定义序列化程序,可回退到默认序列化

时间:2019-11-20 21:18:04

标签: java serialization jackson

在某些情况下,对象可能需要以不同的方式进行序列化。具体来说,一个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来序列化对象...然后将再次调用我的序列化器,这给了我们一个可爱的无限循环,并最终导致了堆栈溢出。 / p>

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

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

2 个答案:

答案 0 :(得分: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...
    }
}

答案 1 :(得分:0)

user_id允许以多种不同方式注册自定义序列化程序。其中之一是使用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;
    }
}