簡體   English   中英

回退到默認序列化的自定義序列化程序

[英]Custom serializer with fallback to default serialization

我有一個案例,object 可能需要以不同的方式進行序列化。 具體來說,REST 端點采用 header,如果 header 為真,我們以一種方式序列化,如果為假,我們以另一種方式序列化。 我認為如果我們將 header 屬性放在 object 中,自定義序列化程序可能是完美的,但是在使用它時遇到了一些麻煩。

(請注意,由於使用了一些 generics 以及代碼的結構方式,因此僅創建不同的 object 將需要大量重構,因此我正在嘗試找到更快的解決方案。)

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

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

並且正在序列化的 class 將被相應地注釋:

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

問題幾乎立即變得明顯: jsonGenerator.writeObject調用ObjectMapper來序列化 object... 然后它會再次調用我的序列化器,最終給我們一個可愛的無限循環和堆棧溢出。

我能想到的解決此問題的唯一選擇是使用反射,遍歷對象的屬性,然后使用 jsonGenerator 將它們寫入新的jsonGenerator 這感覺有點矯枉過正,尤其是當 Jackson 應該能夠為我做到這一點時。 問題是我找不到告訴ObjectMapper忽略 class 上的序列化程序的方法。

有沒有更好的方法來做到這一點? 我想我可以創建一個自定義ObjectMapper以某種方式忽略 class 上注釋的序列化程序,並將其用作jsonGenerator的編解碼器...但不完全確定要拉上ObjectMapper的杠桿。

正如懷疑的那樣,有一種方法可以禁用特定的注釋。 在這種情況下,我想禁用Foo class 上的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...
    }
}

Jackson允許以多種不同方式注冊自定義序列化器。 其中之一是使用com.fasterxml.jackson.databind.ser.BeanSerializerModifier class,它允許創建自定義的基本序列化器,但也可以使用,如果需要,我們也可以使用它。

上面的問題看起來像是OOP問題的一個很好的例子,我們可以解耦POJO ,它的邏輯和序列化過程。 串行器不應該知道如何將 object 轉換為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 model 和序列化器時,我們只需要配置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}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM