当类具有自定义序列化器(Jackson)时,嵌套对象不会被 `SerializerModifier` 处理

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

好的,所以我构建了一个功能自定义

BeanSerializer
,它限制了序列化的深度,我可以通过创建
SerializerModifier
并将其添加到模块来使用它,如图所示。这非常有用,每次遇到另一个嵌套字段时都会调用它,完美地创建
DepthLimitedSerializer
的实例。但是,当我向嵌套类添加自定义序列化器(使用
@JsonSerialize
)时,我的
modifySerializer
方法永远不会在嵌套字段上运行!

这是简单的类层次结构,我们将序列化

Bar
的外部实例:

@Getter @Setter
public class BaseClass {
    private String id;
    private String someBaseProperty;
}
@Getter @Setter
//@JsonSerialize(using = FooSerializer.class)
public class Foo extends BaseClass {
    private String someFooProperty;

}
@Getter @Setter
public class Bar extends BaseClass {
    String someBarProperty;
    Foo fooOfBar;
}

这是您向其传递

maxDepth
的简化自定义序列化器,如果它达到该深度,它只会序列化单个 (
id
) 字段,否则,它只是调用
super
:

public class DepthLimitedSerializer extends BeanSerializer {
    public static int DEFAULT_DEPTH = 2;
    private static final ThreadLocal<Integer> maxDepth = ThreadLocal.withInitial(() -> DEFAULT_DEPTH);
    private static final ThreadLocal<Integer> currentDepth = ThreadLocal.withInitial(() -> -1);

    public DepthLimitedSerializer(BeanSerializerBase src, int depth) {
        super(src);
        maxDepth.set(depth);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (maxDepth.get() < 0 || currentDepth.get() < maxDepth.get()) {
            currentDepth.set(currentDepth.get() + 1);

            super.serializeFields(bean, gen, provider);

            currentDepth.set(currentDepth.get() - 1);
        } else {
            try {
                Arrays.stream(_props).
                    filter(p -> p.getName().equals("id"))
                    .findFirst().orElseThrow()
                    .serializeAsField(bean, gen, provider);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

在这个简单的测试中,我们填充

Foo
Bar
并序列化它们:

class SerializeTest {
    @Test
    void testSerialization() throws JsonProcessingException {
        Foo foo = new Foo();
        foo.setId("fooID");
        foo.setSomeBaseProperty("someBaseValue");
        foo.setSomeFooProperty("foo property value");

        Bar bar = new Bar();
        bar.setId("barId");
        bar.setSomeBaseProperty("base of Bar");
        bar.setFooOfBar(foo);
        bar.setSomeBarProperty("bar property value");

        String depthOfZero = testSerializationToDepthOf(bar, 0);
        System.out.println("depth of ZERO: " + depthOfZero);
        String depthOfOne = testSerializationToDepthOf(bar, 1);
        System.out.println("depth of ONE: " + depthOfOne);
    }

    String testSerializationToDepthOf(BaseClass model, int depth) throws JsonProcessingException {
        ObjectMapper jackson = new ObjectMapper();
        jackson.enable(SerializationFeature.INDENT_OUTPUT);

        SimpleModule module = new SimpleModule("TestModule");
        module.setSerializerModifier(new BeanSerializerModifier() {
            @Override
            public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
                                                      JsonSerializer<?> serializer) {
                if (BaseClass.class.isAssignableFrom(beanDesc.getType().getRawClass())) {
                    return new DepthLimitedSerializer((BeanSerializerBase) serializer, depth);
                }
                return super.modifySerializer(config, beanDesc, serializer);
            }
        });

        jackson.registerModule(module);
        return jackson.writeValueAsString(model);
    }
}

这正是我们所期望的,请注意,当

depth
0
时,我们只能得到嵌套
id
Foo fooOfBar
字段,这是正确的,但
depth
1
,我们得到完整的输出。这适用于任意深度,并且当它运行时,该
modifySerializer
方法以及
DepthLimitedSerializer
中的方法都针对每个嵌套模型运行!

depth of ZERO: {
  "id" : "barId",
  "someBaseProperty" : "base of Bar",
  "someBarProperty" : "bar property value",
  "fooOfBar" : {
    "id" : "fooID"
  }
}
depth of ONE: {
  "id" : "barId",
  "someBaseProperty" : "base of Bar",
  "someBarProperty" : "bar property value",
  "fooOfBar" : {
    "id" : "fooID",
    "someBaseProperty" : "someBaseValue",
    "someFooProperty" : "foo property value"
  }
}

但是!如果

BaseClass
的这些子类之一需要自定义序列化,并且我尝试为特定类添加自定义序列化程序,例如
Foo
:

public class FooSerializer extends StdSerializer<Foo> {

    public FooSerializer() {
        super(Foo.class);
    }

    public void serialize(Foo foo, JsonGenerator jgen, SerializerProvider serializerProvider)
            throws IOException {
        jgen.writeStartObject();
        jgen.writeStringField("custom", foo.getId() + "!" + foo.getSomeFooProperty());
        jgen.writeEndObject();
    }
}

我取消注释上面的

@JsonSerialize(using = FooSerializer.class)
行并将自定义序列化器分配给该类,然后我的
modifySerializer
方法和所有其他方法永远不会为我的自定义注释嵌套类运行,因此
depth
被忽略,自定义序列化器总是运行:

depth of ZERO: {
  "id" : "barId",
  "someBaseProperty" : "base of Bar",
  "someBarProperty" : "bar property value",
  "fooOfBar" : {
    "custom" : "fooID!foo property value"
  }
}
depth of ONE: {
  "id" : "barId",
  "someBaseProperty" : "base of Bar",
  "someBarProperty" : "bar property value",
  "fooOfBar" : {
    "custom" : "fooID!foo property value"
  }
}

我期望的行为是

modifySerializer
方法仍然运行,然后
super.modifySerializer()
调用发现
FooSerializer
,但它只针对顶级对象(以及其他对象,不是那么自定义注释)。

我怎样才能实现这种行为?我尝试制作自定义序列化器

extends 
DepthLimitedSerializer
, but they are of different types of Jackson 
Serializer`,但到目前为止我无法协调它们并让它们以正确的顺序一起工作!显然我不能使用注释来分配序列化器,但我该怎么办?

谢谢大家。

java serialization jackson jackson-databind jackson-modules
1个回答
0
投票

这是我的最终工作解决方案。我几乎认为这是一个错误,通过注释分配的序列化器没有被附加到

SerializerModifiers
ObjectMapper
处理。文档中没有任何内容说他们会“修改序列化器,但是哦,不是那些序列化器”。我的解决方案有点hacky,但它有效。

首先,如果通过注释找到序列化器,则自定义

BeanSerializerFactory
添加运行修饰符。无论如何,这种情况发生在被重写方法的开头。

public class FixedBeanSerializerFactory extends BeanSerializerFactory {
    public FixedBeanSerializerFactory(SerializerFactoryConfig config) {
        super(config);
    }

    @Override
    public JsonSerializer<Object> createSerializer(SerializerProvider prov, JavaType origType)
            throws JsonMappingException {

        // Very first thing, let's check if there is explicit serializer annotation:
        final SerializationConfig config = prov.getConfig();
        BeanDescription beanDesc = config.introspect(origType);
        JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
        if (ser != null) {
            if (_factoryConfig.hasSerializerModifiers()) {
                for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
                    ser = mod.modifySerializer(config, beanDesc, ser);
                }
            }
            return (JsonSerializer<Object>) ser;
        }

        return super.createSerializer(prov, origType);
    }

    @Override
    public SerializerFactory withConfig(SerializerFactoryConfig config) {
        return new FixedBeanSerializerFactory(config);
    }
}

现在,这个

DepthLimitedSerializer
比它需要的要长很多,主要是因为我必须复制
BeanSerailzier
中的很多代码。你为什么问?因为
serialize
中的
BeanSerializer
方法是最终的,所以我无法重写它。相反,我被迫覆盖
BeanSerializerBase
并从中复制一堆代码。

public class DepthLimitedSerializer extends BeanSerializerBase {
    public static final int DEFAULT_DEPTH = 2;

    private static final ObjectMapper jackson = new ObjectMapper();
    private static final ThreadLocal<Integer> maxDepth = ThreadLocal.withInitial(() -> DEFAULT_DEPTH);
    private static final ThreadLocal<Integer> currentDepth = ThreadLocal.withInitial(() -> -1);

    public static JsonSerializer<Object> forSerializer(JsonSerializer<Object> serializer,
                                                       BeanDescription beanDesc,
                                                       int depth) {

        if (serializer instanceof BeanSerializerBase) {
            return new DepthLimitedSerializer((BeanSerializerBase) serializer, depth);

        } else {
            BeanSerializerBuilder builder = new BeanSerializerBuilder(beanDesc);
            JavaType type = jackson.constructType(serializer.handledType());
            BeanPropertyWriter[] properties = {};
            BeanPropertyWriter[] filteredProperties = {};
            maxDepth.set(depth);
            return new DepthLimitedSerializer(serializer, type, builder, properties, filteredProperties);
        }
    }

    protected JsonSerializer<Object> src;

    public DepthLimitedSerializer(BeanSerializerBase src, int depth) {
        super(src);
        this.src = src;
        maxDepth.set(depth);
    }

    protected DepthLimitedSerializer(DepthLimitedSerializer depthLimitedSerializer,
                                     BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
        super(depthLimitedSerializer, properties, filteredProperties);
        this.src = depthLimitedSerializer;
    }

    protected DepthLimitedSerializer(JsonSerializer<Object> src, JavaType type, BeanSerializerBuilder builder,
                                     BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
        super(type, builder, properties, filteredProperties);
        this.src = src;
    }

    protected DepthLimitedSerializer(BeanSerializerBase src, ObjectIdWriter objectIdWriter, Object filterId) {
        super(src, objectIdWriter, filterId);
        this.src = src;
    }

    protected DepthLimitedSerializer(BeanSerializerBase src,
                                     BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
        super(src, properties, filteredProperties);
        this.src = src;
    }

    @Override
    public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
        return new DepthLimitedSerializer(this, objectIdWriter, _propertyFilterId);
    }

    @Override
    protected BeanSerializerBase withByNameInclusion(Set<String> toIgnore, Set<String> toInclude) {
        return null;
    }

    @Override // @since 2.11.1
    protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties,
                                                BeanPropertyWriter[] filteredProperties) {
        return new DepthLimitedSerializer(this, properties, filteredProperties);
    }

    @Override
    protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (maxDepth.get() < 0 || currentDepth.get() < maxDepth.get()) {
            currentDepth.set(currentDepth.get() + 1);

            super.serializeFields(bean, gen, provider);

            currentDepth.set(currentDepth.get() - 1);
        } else {
            try {
                JsonDepthLimited depthAnnotation = bean.getClass().getAnnotation(JsonDepthLimited.class);
                String defaultFieldName = depthAnnotation.defaultField();
                boolean includeNulls = depthAnnotation.includeNulls();
                if (StringUtils.isNotEmpty(defaultFieldName)) {
                    if (!includeNulls) {
                        Arrays.stream(_props).
                                filter(p -> p.getName().equals(defaultFieldName))
                                .findFirst().orElseThrow()
                                .serializeAsField(bean, gen, provider);
                    } else {
                        Arrays.stream(_props).forEach(p -> {
                            try {
                                if (p.getName().equals(defaultFieldName)) {
                                    p.serializeAsField(bean, gen, provider);
                                } else {
                                    gen.writeNullField(p.getName());
                                }
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        });
                    }

                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    protected BeanSerializerBase asArraySerializer() {
        if ((_objectIdWriter == null)
                && (_anyGetterWriter == null)
                && (_propertyFilterId == null)
        ) {
            return new BeanAsArraySerializer(this);
        }
        // already is one, so:
        return this;
    }

    @Override
    public BeanSerializerBase withFilterId(Object filterId) {
        return new DepthLimitedSerializer(this, _objectIdWriter, filterId);
    }

    @Override
    public void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)
            throws IOException {

        if (src == null || src instanceof BeanSerializerBase) {
            if (_objectIdWriter != null) {
                gen.setCurrentValue(bean); // [databind#631]
                _serializeWithObjectId(bean, gen, provider, true);
                return;
            }
            gen.writeStartObject(bean);
            if (_propertyFilterId != null) {
                serializeFieldsFiltered(bean, gen, provider);
            } else {
                serializeFields(bean, gen, provider);
            }
            gen.writeEndObject();

        } else {
            if (maxDepth.get() < 0 || currentDepth.get() < maxDepth.get()) {
                Class<?> t = src.handledType();
                src.serialize(t.cast(bean), gen, provider);
            } else {
                gen.writeNull();
            }
        }

    }

    @Override public String toString() {
        return "DepthAwareSerializer for " + handledType().getName();
    }
}

此注释标记了包含的类,并让我们传递一些参数。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JsonDepthLimited {
    String defaultField() default StringUtils.EMPTY;
    boolean includeNulls() default false;
    Class<? extends JsonSerializer> serializer() default DepthLimitedSerializer.class;
}

这是我们要演示的 POJO 层次结构:

@JsonDepthLimited(defaultField = "id")
@Getter @Setter
public class BaseClass {
    private String id;
    private String someBaseProperty;
}
@Getter @Setter
@JsonSerialize(using = FooSerializer.class)
public class Foo extends BaseClass {
    private String someFooProperty;
}
@Getter @Setter
public class Bar extends BaseClass {
    String someBarProperty;
    Foo fooOfBar;
}

Foo
的自定义序列化器:

public class FooSerializer extends StdSerializer<Foo> {
    protected FooSerializer() {
        super(Foo.class);
    }

    public void serialize(Foo foo, JsonGenerator jgen, SerializerProvider serializerProvider)
            throws IOException {
        jgen.writeStartObject();
        jgen.writeStringField("custom", foo.getId() + "!" + foo.getSomeFooProperty());
        jgen.writeEndObject();
    }
}

最后是演示:


class SerializeTest {
    @Test
    void testSerialization() throws JsonProcessingException {
        Foo foo = new Foo();
        foo.setId("fooID");
        foo.setSomeBaseProperty("someBaseValue");
        foo.setSomeFooProperty("foo property value");

        Bar bar = new Bar();
        bar.setId("barId");
        bar.setSomeBaseProperty("base of Bar");
        bar.setFooOfBar(foo);
        bar.setSomeBarProperty("bar property value");

        String depthOfZero = testSerializationToDepthOf(bar, 0);
        System.out.println("depth of ZERO: " + depthOfZero);
        String depthOfOne = testSerializationToDepthOf(bar, 1);
        System.out.println("depth of ONE: " + depthOfOne);
    }

    String testSerializationToDepthOf(BaseClass model, int depth) throws JsonProcessingException {
        ObjectMapper jackson = new ObjectMapper();
        jackson.enable(SerializationFeature.INDENT_OUTPUT);
        SerializerFactoryConfig factoryConfig = new SerializerFactoryConfig();
        jackson.setSerializerFactory(new FixedBeanSerializerFactory(factoryConfig));

        if (depth >= 0) {
            SimpleModule fragmentModule = new SimpleModule("FragmentModule");
            BeanSerializerModifier modifier = new BeanSerializerModifier() {
                @Override
                public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
                                                          JsonSerializer<?> serializer) {
                    if (beanDesc.getClassAnnotations().has(JsonDepthLimited.class)) {

                        return DepthLimitedSerializer
                                .forSerializer((JsonSerializer<Object>) serializer, beanDesc, depth);
                    }
                    return serializer;
                }
            };

            fragmentModule.setSerializerModifier(modifier);
            jackson.registerModule(fragmentModule);
        }

        return jackson.writeValueAsString(model);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.