好的,所以我构建了一个功能自定义
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`,但到目前为止我无法协调它们并让它们以正确的顺序一起工作!显然我不能使用注释来分配序列化器,但我该怎么办?
谢谢大家。
这是我的最终工作解决方案。我几乎认为这是一个错误,通过注释分配的序列化器没有被附加到
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);
}
}